X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fdevices%2Fswf.c;h=b090c7bfe52e10b261da2963eea36c703f37f148;hp=2969e750abfb8eaec01139ef2c611d83df9447a1;hb=7fb4a4ac393f19a0b8a8998a2f1deac88c97eda0;hpb=fc0b3bbd680ad6aee83ff13cb57f5016a442d0e2 diff --git a/lib/devices/swf.c b/lib/devices/swf.c index 2969e75..b090c7b 100644 --- a/lib/devices/swf.c +++ b/lib/devices/swf.c @@ -42,20 +42,33 @@ #include "../gfxtools.h" #include "swf.h" #include "../gfxpoly.h" -#include "../png.h" +#include "../gfximage.h" -#define CHARDATAMAX 8192 +#define CHARDATAMAX 1024 #define CHARMIDX 0 #define CHARMIDY 0 -typedef struct _chardata { +typedef struct _charatposition { int charid; - int fontid; /* TODO: use a SWFFONT instead */ + SWFFONT*font; int x; int y; int size; RGBA color; -} chardata_t; +} charatposition_t; + +typedef struct _chararray { + charatposition_t chr[CHARDATAMAX+1]; + int pos; + struct _chararray *next; +} chararray_t; + +typedef struct _charbuffer { + MATRIX matrix; + chararray_t*array; + chararray_t*last; + struct _charbuffer *next; +} charbuffer_t; typedef struct _fontlist { @@ -77,6 +90,7 @@ typedef struct _swfoutput_internal double config_ppmsubpixels; double config_jpegsubpixels; char hasbuttons; + int config_invisibletexttofront; int config_dots; int config_simpleviewer; int config_opennewwindow; @@ -88,7 +102,9 @@ typedef struct _swfoutput_internal int config_storeallcharacters; int config_enablezlib; int config_insertstoptag; + int config_showimages; int config_watermark; + int config_noclips; int config_flashversion; int config_reordertags; int config_showclipshapes; @@ -99,6 +115,7 @@ typedef struct _swfoutput_internal int config_bboxvars; int config_disable_polygon_conversion; int config_normalize_polygon_positions; + int config_alignfonts; char config_disablelinks; RGBA config_linkcolor; float config_minlinewidth; @@ -126,7 +143,7 @@ typedef struct _swfoutput_internal SHAPE* shape; int shapeid; - int textid; + int textmode; int watermarks; @@ -166,8 +183,9 @@ typedef struct _swfoutput_internal SRECT pagebbox; - chardata_t chardata[CHARDATAMAX]; - int chardatapos; + charbuffer_t* chardata; + charbuffer_t* topchardata; //chars supposed to be above everything else + int firstpage; char pagefinished; @@ -187,6 +205,8 @@ typedef struct _swfoutput_internal char* mark; } swfoutput_internal; + +static const int NO_FONT3=0; static void swf_fillbitmap(gfxdevice_t*driver, gfxline_t*line, gfximage_t*img, gfxmatrix_t*move, gfxcxform_t*cxform); static int swf_setparameter(gfxdevice_t*driver, const char*key, const char*value); @@ -202,6 +222,10 @@ static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font); static void swf_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action); static void swf_startframe(gfxdevice_t*dev, int width, int height); static void swf_endframe(gfxdevice_t*dev); +static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points); +static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points); +static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points); + static gfxresult_t* swf_finish(gfxdevice_t*driver); static swfoutput_internal* init_internal_struct() @@ -216,7 +240,7 @@ static swfoutput_internal* init_internal_struct() i->startdepth = 0; i->linewidth = 0; i->shapeid = -1; - i->textid = -1; + i->textmode = 0; i->frameno = 0; i->lastframeno = 0; @@ -234,7 +258,7 @@ static swfoutput_internal* init_internal_struct() i->fillstylechanged = 0; i->bboxrectpos = -1; - i->chardatapos = 0; + i->chardata = 0; i->firstpage = 1; i->pagefinished = 1; @@ -313,12 +337,38 @@ typedef struct _plotxy double x,y; } plotxy_t; +static inline int twipsnap(double f) +{ + /* if(f < -0x40000000/20.0) { + fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f); + f = -0x40000000/20.0; + } else if(f>0x3fffffff/20.0) { + fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f); + f = 0x3fffffff/20.0; + }*/ + + /* clamp coordinates to a rectangle with the property that we + can represent a line from the upper left corner to the upper + right corner using no more than 64 strokes */ + const double min = -(1<<(18+4))/20.0; + const double max = ((1<<(18+4))-1)/20.0; + if(f < min) { + fprintf(stderr, "Warning: Coordinate underflow (%f)\n", f); + f = min; + } else if(f>max) { + fprintf(stderr, "Warning: Coordinate overflow (%f)\n", f); + f = max; + } + + return (int)(f*20); +} + // write a move-to command into the swf static int movetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; - int rx = (int)(p0.x*20); - int ry = (int)(p0.y*20); + int rx = twipsnap(p0.x); + int ry = twipsnap(p0.y); if(rx!=i->swflastx || ry!=i->swflasty || i->fillstylechanged) { swf_ShapeSetMove (tag, i->shape, rx,ry); i->fillstylechanged = 0; @@ -380,8 +430,8 @@ static void addPointToBBox(gfxdevice_t*dev, int px, int py) static void linetoxy(gfxdevice_t*dev, TAG*tag, plotxy_t p0) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; - int px = (int)(p0.x*20); - int py = (int)(p0.y*20); + int px = twipsnap(p0.x); + int py = twipsnap(p0.y); int rx = (px-i->swflastx); int ry = (py-i->swflasty); if(rx|ry) { @@ -419,12 +469,12 @@ static void splineto(gfxdevice_t*dev, TAG*tag, plotxy_t control,plotxy_t end) int lastlastx = i->swflastx; int lastlasty = i->swflasty; - int cx = ((int)(control.x*20)-i->swflastx); - int cy = ((int)(control.y*20)-i->swflasty); + int cx = (twipsnap(control.x)-i->swflastx); + int cy = (twipsnap(control.y)-i->swflasty); i->swflastx += cx; i->swflasty += cy; - int ex = ((int)(end.x*20)-i->swflastx); - int ey = ((int)(end.y*20)-i->swflasty); + int ex = (twipsnap(end.x)-i->swflastx); + int ey = (twipsnap(end.y)-i->swflasty); i->swflastx += ex; i->swflasty += ey; @@ -479,7 +529,7 @@ static void startFill(gfxdevice_t*dev) } } -static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b) +static inline int colorcompare(RGBA*a,RGBA*b) { if(a->r!=b->r || @@ -491,58 +541,57 @@ static inline int colorcompare(gfxdevice_t*dev, RGBA*a,RGBA*b) return 1; } -static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m) +static SRECT getcharacterbbox(chararray_t*chardata, MATRIX* m, int flashversion) { - swfoutput_internal*i = (swfoutput_internal*)dev->internal; SRECT r; char debug = 0; memset(&r, 0, sizeof(r)); int t; if(debug) printf("\n"); - for(t=0;tchardatapos;t++) - { - if(i->chardata[t].fontid != font->id) { - msg(" Internal error: fontid %d != fontid %d", i->chardata[t].fontid, font->id); - exit(1); + + double div = 1.0 / 1024.0; + if(flashversion>=8 && !NO_FONT3) { + div = 1.0 / 20480.0; + } + + while(chardata) { + for(t=0;tpos;t++) { + charatposition_t*chr = &chardata->chr[t]; + SRECT b = chr->font->layout->bounds[chr->charid]; + b.xmin = floor((b.xmin*(double)chr->size) *div); + b.ymin = floor((b.ymin*(double)chr->size) *div); + b.xmax = ceil((b.xmax*(double)chr->size) *div); + b.ymax = ceil((b.ymax*(double)chr->size) *div); + + b.xmin += chr->x; + b.ymin += chr->y; + b.xmax += chr->x; + b.ymax += chr->y; + + /* until we solve the INTERNAL_SCALING problem (see below) + make sure the bounding box is big enough */ + b.xmin -= 20; + b.ymin -= 20; + b.xmax += 20; + b.ymax += 20; + + b = swf_TurnRect(b, m); + + if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d, char %d]\n", + chr->font->layout->bounds[chr->charid].xmin/20.0, + chr->font->layout->bounds[chr->charid].ymin/20.0, + chr->font->layout->bounds[chr->charid].xmax/20.0, + chr->font->layout->bounds[chr->charid].ymax/20.0, + b.xmin/20.0, + b.ymin/20.0, + b.xmax/20.0, + b.ymax/20.0, + chr->font->id, + chr->charid); + swf_ExpandRect2(&r, &b); } - SRECT b = font->layout->bounds[i->chardata[t].charid]; - b.xmin *= i->chardata[t].size; - b.ymin *= i->chardata[t].size; - b.xmax *= i->chardata[t].size; - b.ymax *= i->chardata[t].size; - - /* divide by 1024, rounding xmax/ymax up */ - b.xmax += 1023; b.ymax += 1023; b.xmin /= 1024; b.ymin /= 1024; b.xmax /= 1024; b.ymax /= 1024; - - b.xmin += i->chardata[t].x; - b.ymin += i->chardata[t].y; - b.xmax += i->chardata[t].x; - b.ymax += i->chardata[t].y; - - /* until we solve the INTERNAL_SCALING problem (see below) - make sure the bounding box is big enough */ - b.xmin -= 20; - b.ymin -= 20; - b.xmax += 20; - b.ymax += 20; - - b = swf_TurnRect(b, m); - - if(debug) printf("(%f,%f,%f,%f) -> (%f,%f,%f,%f) [font %d/%d, char %d]\n", - font->layout->bounds[i->chardata[t].charid].xmin/20.0, - font->layout->bounds[i->chardata[t].charid].ymin/20.0, - font->layout->bounds[i->chardata[t].charid].xmax/20.0, - font->layout->bounds[i->chardata[t].charid].ymax/20.0, - b.xmin/20.0, - b.ymin/20.0, - b.xmax/20.0, - b.ymax/20.0, - i->chardata[t].fontid, - font->id, - i->chardata[t].charid - ); - swf_ExpandRect2(&r, &b); + chardata = chardata->next; } if(debug) printf("-----> (%f,%f,%f,%f)\n", r.xmin/20.0, @@ -552,20 +601,31 @@ static SRECT getcharacterbbox(gfxdevice_t*dev, SWFFONT*font, MATRIX* m) return r; } -static void putcharacters(gfxdevice_t*dev, TAG*tag) +static chararray_t*chararray_reverse(chararray_t*buf) +{ + chararray_t*prev = 0; + while(buf) { + chararray_t*next = buf->next; + buf->next = prev; + prev = buf; + buf = next; + } + return prev; +} + +static void chararray_writetotag(chararray_t*_chardata, TAG*tag) { - swfoutput_internal*i = (swfoutput_internal*)dev->internal; - int t; SWFFONT font; RGBA color; - color.r = i->chardata[0].color.r^255; + color.r = _chardata?_chardata->chr[0].color.r^255:0; color.g = 0; color.b = 0; color.a = 0; - int lastfontid; + SWFFONT*lastfont; int lastx; int lasty; int lastsize; + int lastchar; int charids[128]; int charadvance[128]; int charstorepos; @@ -575,20 +635,21 @@ static void putcharacters(gfxdevice_t*dev, TAG*tag) if(tag->id != ST_DEFINETEXT && tag->id != ST_DEFINETEXT2) { - msg(" internal error: putcharacters needs an text tag, not %d\n",tag->id); + msg(" internal error: charbuffer_put needs an text tag, not %d\n",tag->id); exit(1); } - if(!i->chardatapos) { - msg(" putcharacters called with zero characters"); + if(!_chardata) { + msg(" charbuffer_put called with zero characters"); } for(pass = 0; pass < 2; pass++) { charstorepos = 0; - lastfontid = -1; + lastfont = 0; lastx = CHARMIDX; lasty = CHARMIDY; lastsize = -1; + lastchar = -1; if(pass==1) { @@ -597,114 +658,157 @@ static void putcharacters(gfxdevice_t*dev, TAG*tag) swf_SetU8(tag, advancebits); } - for(t=0;t<=i->chardatapos;t++) - { - if(lastfontid != i->chardata[t].fontid || - lastx!=i->chardata[t].x || - lasty!=i->chardata[t].y || - !colorcompare(dev,&color, &i->chardata[t].color) || - charstorepos==127 || - lastsize != i->chardata[t].size || - t == i->chardatapos) - { - if(charstorepos && pass==0) - { - int s; - for(s=0;s=(1<=(1<writeBit = 0; // Q&D - swf_SetBits(tag, 0, 1); // GLYPH Record - swf_SetBits(tag, charstorepos, 7); // number of glyphs - int s; - for(s=0;schardatapos) - { - RGBA*newcolor=0; - SWFFONT*newfont=0; - int newx = 0; - int newy = 0; - if(lastx != i->chardata[t].x || - lasty != i->chardata[t].y) - { - newx = i->chardata[t].x; - newy = i->chardata[t].y; - if(newx == 0) - newx = SET_TO_ZERO; - if(newy == 0) - newy = SET_TO_ZERO; - } - if(!colorcompare(dev,&color, &i->chardata[t].color)) - { - color = i->chardata[t].color; - newcolor = &color; - } - font.id = i->chardata[t].fontid; - if(lastfontid != i->chardata[t].fontid || lastsize != i->chardata[t].size) - newfont = &font; - - tag->writeBit = 0; // Q&D - swf_TextSetInfoRecord(tag, newfont, i->chardata[t].size, newcolor, newx,newy); - } - - lastfontid = i->chardata[t].fontid; - lastx = i->chardata[t].x; - lasty = i->chardata[t].y; - lastsize = i->chardata[t].size; - } - - if(t==i->chardatapos) - break; - - int advance; - int nextt = t==i->chardatapos-1?t:t+1; - int rel = i->chardata[nextt].x-i->chardata[t].x; - if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) { - advance = rel; - lastx=i->chardata[nextt].x; - } - else { - advance = 0; - lastx=i->chardata[t].x; - } - charids[charstorepos] = i->chardata[t].charid; - charadvance[charstorepos] = advance; - charstorepos ++; - } + chararray_t*chardata = _chardata; + + while(chardata) { + int t; + + assert(!chardata->next || chardata->pos == CHARDATAMAX); + assert(chardata->pos); + + int to = chardata->next?chardata->pos-1:chardata->pos; + + for(t=0;t<=to;t++) + { + char islast = t==chardata->pos; + + charatposition_t*chr = &chardata->chr[t]; + + if(lastfont != chr->font || + lastx!=chr->x || + lasty!=chr->y || + !colorcompare(&color, &chardata->chr[t].color) || + charstorepos==127 || + lastsize != chardata->chr[t].size || + islast) + { + if(charstorepos && pass==0) + { + int s; + for(s=0;s=(1<=(1<writeBit = 0; // Q&D + swf_SetBits(tag, 0, 1); // GLYPH Record + swf_SetBits(tag, charstorepos, 7); // number of glyphs + int s; + for(s=0;sx || + lasty != chr->y) + { + newx = chr->x; + newy = chr->y; + if(newx == 0) + newx = SET_TO_ZERO; + if(newy == 0) + newy = SET_TO_ZERO; + } + if(!colorcompare(&color, &chr->color)) + { + color = chr->color; + newcolor = &color; + } + font.id = chr->font->id; + if(lastfont != chr->font || lastsize != chr->size) + newfont = &font; + + tag->writeBit = 0; // Q&D + swf_TextSetInfoRecord(tag, newfont, chr->size, newcolor, newx, newy); + } + + lastfont = chr->font; + lastx = chr->x; + lasty = chr->y; + lastsize = chr->size; + } + + if(islast) + break; + + int nextx = chr->x; + if(tpos-1) nextx = chardata->chr[t+1].x; + if(t==chardata->pos-1 && chardata->next) nextx = chardata->next->chr[0].x; + int dx = nextx-chr->x; + + int advance; + if(dx>=0 && (dx<(1<<(advancebits-1)) || pass==0)) { + advance = dx; + lastx=nextx; + } else { + advance = 0; + lastx=chr->x; + } + + charids[charstorepos] = chr->charid; + charadvance[charstorepos] = advance; + lastchar = chr->charid; + charstorepos ++; + } + chardata = chardata->next; + } } - i->chardatapos = 0; } -static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, int size, RGBA color) +static void chararray_destroy(chararray_t*chr) { - swfoutput_internal*i = (swfoutput_internal*)dev->internal; - if(i->chardatapos == CHARDATAMAX) - { - msg(" Character buffer too small. SWF will be slightly bigger"); - endtext(dev); - starttext(dev); + while(chr) { + chararray_t*next = chr->next; + chr->next = 0; + free(chr); + chr = next; } - i->chardata[i->chardatapos].fontid = fontid; - i->chardata[i->chardatapos].charid = charid; - i->chardata[i->chardatapos].x = x; - i->chardata[i->chardatapos].y = y; - i->chardata[i->chardatapos].color = color; - i->chardata[i->chardatapos].size = size; - i->chardatapos++; +} + +static inline int matrix_diff(MATRIX*m1, MATRIX*m2) +{ + return memcmp(m1,m2,sizeof(MATRIX)); +} +static charbuffer_t*charbuffer_append(charbuffer_t*buf, SWFFONT*font, int charid, int x,int y, int size, RGBA color, MATRIX*m) +{ + if(!buf || matrix_diff(&buf->matrix,m)) { + charbuffer_t*n = rfx_calloc(sizeof(charbuffer_t)); + n->matrix = *m; + n->next = buf; + buf = n; + } + if(!buf->last || buf->last->pos == CHARDATAMAX) { + chararray_t*n = rfx_calloc(sizeof(chararray_t)); + if(!buf->array) { + buf->array = buf->last = n; + } else { + buf->last->next = n; + buf->last = n; + } + } + chararray_t*a = buf->last; + a->chr[a->pos].font = font; + a->chr[a->pos].charid = charid; + a->chr[a->pos].x = x; + a->chr[a->pos].y = y; + a->chr[a->pos].color = color; + a->chr[a->pos].size = size; + a->pos++; + return buf; } /* Notice: we can only put chars in the range -1639,1638 (-32768/20,32768/20). @@ -712,30 +816,27 @@ static void putcharacter(gfxdevice_t*dev, int fontid, int charid, int x,int y, i If we set it to low, however, the char positions will be inaccurate */ #define GLYPH_SCALE 1 -static void endtext(gfxdevice_t*dev) +static void chararray_writetodev(gfxdevice_t*dev, chararray_t*array, MATRIX*matrix, char invisible) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; - if(i->textid<0) - return; - + + int textid = getNewID(dev); i->tag = swf_InsertTag(i->tag,ST_DEFINETEXT2); - swf_SetU16(i->tag, i->textid); - + swf_SetU16(i->tag, textid); SRECT r; - r = getcharacterbbox(dev, i->swffont, &i->fontmatrix); + r = getcharacterbbox(array, matrix, i->config_flashversion); r = swf_ClipRect(i->pagebbox, r); swf_SetRect(i->tag,&r); + swf_SetMatrix(i->tag, matrix); + msg(" Placing text as ID %d", textid); + chararray_writetotag(array, i->tag); + i->chardata = 0; - swf_SetMatrix(i->tag,&i->fontmatrix); - - msg(" Placing text (%d characters) as ID %d", i->chardatapos, i->textid); - - putcharacters(dev, i->tag); swf_SetU8(i->tag,0); if(i->swf->fileVersion >= 8) { i->tag = swf_InsertTag(i->tag, ST_CSMTEXTSETTINGS); - swf_SetU16(i->tag, i->textid); + swf_SetU16(i->tag, textid); //swf_SetU8(i->tag, /*subpixel grid*/(2<<3)|/*flashtype*/0x40); swf_SetU8(i->tag, /*grid*/(1<<3)|/*flashtype*/0x40); @@ -743,51 +844,37 @@ static void endtext(gfxdevice_t*dev) swf_SetU32(i->tag, 0);//thickness swf_SetU32(i->tag, 0);//sharpness + //swf_SetU32(i->tag, 0x20000);//thickness + //swf_SetU32(i->tag, 0x800000);//sharpness swf_SetU8(i->tag, 0);//reserved } - i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2); - - swf_ObjectPlace(i->tag,i->textid,getNewDepth(dev),&i->page_matrix,NULL,NULL); - i->textid = -1; + if(invisible && i->config_flashversion>=8) { + i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT3); + swf_ObjectPlaceBlend(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL,BLENDMODE_MULTIPLY); + } else { + i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2); + swf_ObjectPlace(i->tag,textid,getNewDepth(dev),&i->page_matrix,NULL,NULL); + } } -/* set's the matrix which is to be applied to characters drawn by swfoutput_drawchar() */ -static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force) +static void charbuffer_writetodevandfree(gfxdevice_t*dev, charbuffer_t*buf, char invisible) +{ + while(buf) { + charbuffer_t*next = buf->next;buf->next = 0; + chararray_writetodev(dev, buf->array, &buf->matrix, invisible); + chararray_destroy(buf->array); + free(buf); + buf = next; + } +} + +static void endtext(gfxdevice_t*dev) { - m11 *= 1024; - m12 *= 1024; - m21 *= 1024; - m22 *= 1024; swfoutput_internal*i = (swfoutput_internal*)dev->internal; - if(i->lastfontm11 == m11 && - i->lastfontm12 == m12 && - i->lastfontm21 == m21 && - i->lastfontm22 == m22 && !force) + if(!i->textmode) return; - if(i->textid>=0) - endtext(dev); - - i->lastfontm11 = m11; - i->lastfontm12 = m12; - i->lastfontm21 = m21; - i->lastfontm22 = m22; - - double xsize = sqrt(m11*m11 + m12*m12); - double ysize = sqrt(m21*m21 + m22*m22); - i->current_font_size = (xsize>ysize?xsize:ysize)*1; - if(i->current_font_size < 1) - i->current_font_size = 1; - double ifs = 1.0 / (i->current_font_size*GLYPH_SCALE); - - MATRIX m; - m.sx = (S32)((m11*ifs)*65536); m.r1 = (S32)((m21*ifs)*65536); - m.r0 = (S32)((m12*ifs)*65536); m.sy = (S32)((m22*ifs)*65536); - /* this is the position of the first char to set a new fontmatrix- - we hope that it's close enough to all other characters using the - font, so we use its position as origin for the matrix */ - m.tx = x*20; - m.ty = y*20; - i->fontmatrix = m; + charbuffer_writetodevandfree(dev, i->chardata, 0);i->chardata = 0; + i->textmode = 0; } static int watermark2_width=47; @@ -868,11 +955,16 @@ static void endpage(gfxdevice_t*dev) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; if(i->pagefinished) - return; + return; + if(i->shapeid>=0) - endshape(dev); - if(i->textid>=0) - endtext(dev); + endshape(dev); + if(i->textmode) + endtext(dev); + if(i->topchardata) { + charbuffer_writetodevandfree(dev, i->topchardata, 1); + i->topchardata=0; + } while(i->clippos) dev->endclip(dev); @@ -1002,6 +1094,7 @@ void swf_startframe(gfxdevice_t*dev, int width, int height) i->lastframeno = i->frameno; i->firstpage = 0; i->pagefinished = 0; + i->chardata = 0; } void swf_endframe(gfxdevice_t*dev) @@ -1172,7 +1265,7 @@ static void starttext(gfxdevice_t*dev) if(i->config_watermark) { insert_watermark(dev, 0); } - i->textid = getNewID(dev); + i->textmode = 1; i->swflastx=i->swflasty=0; } @@ -1379,6 +1472,8 @@ void swfoutput_finalize(gfxdevice_t*dev) endpage(dev); fontlist_t *iterator = i->fontlist; + char use_font3 = i->config_flashversion>=8 && !NO_FONT3; + while(iterator) { TAG*mtag = i->swf->firstTag; if(iterator->swffont) { @@ -1388,16 +1483,25 @@ void swfoutput_finalize(gfxdevice_t*dev) } int used = iterator->swffont->use && iterator->swffont->use->used_glyphs; if(used) { - mtag = swf_InsertTag(mtag, ST_DEFINEFONT2); - swf_FontSetDefine2(mtag, iterator->swffont); + if(!use_font3) { + mtag = swf_InsertTag(mtag, ST_DEFINEFONT2); + swf_FontSetDefine2(mtag, iterator->swffont); + } else { + mtag = swf_InsertTag(mtag, ST_DEFINEFONT3); + swf_FontSetDefine2(mtag, iterator->swffont); + } } } iterator = iterator->next; } - + i->tag = swf_InsertTag(i->tag,ST_END); TAG* tag = i->tag->prev; + + if(use_font3 && i->config_storeallcharacters && i->config_alignfonts) { + swf_FontPostprocess(i->swf); // generate alignment information + } /* remove the removeobject2 tags between the last ST_SHOWFRAME and the ST_END- they confuse the flash player */ @@ -1416,7 +1520,7 @@ void swfoutput_finalize(gfxdevice_t*dev) /* Add AVM2 actionscript */ if(i->config_flashversion>=9 && - (i->config_insertstoptag || i->hasbuttons)) { + (i->config_insertstoptag || i->hasbuttons) && !i->config_linknameurl) { swf_AddButtonLinks(i->swf, i->config_insertstoptag, i->config_internallinkfunction||i->config_externallinkfunction); } @@ -1451,17 +1555,17 @@ void* swfresult_get(gfxresult_t*gfx, const char*name) if(!strcmp(name, "swf")) { return (void*)swf_CopySWF(swf); } else if(!strcmp(name, "xmin")) { - return (void*)(swf->movieSize.xmin/20); + return (void*)(ptroff_t)(swf->movieSize.xmin/20); } else if(!strcmp(name, "ymin")) { - return (void*)(swf->movieSize.ymin/20); + return (void*)(ptroff_t)(swf->movieSize.ymin/20); } else if(!strcmp(name, "xmax")) { - return (void*)(swf->movieSize.xmax/20); + return (void*)(ptroff_t)(swf->movieSize.xmax/20); } else if(!strcmp(name, "ymax")) { - return (void*)(swf->movieSize.ymax/20); + return (void*)(ptroff_t)(swf->movieSize.ymax/20); } else if(!strcmp(name, "width")) { - return (void*)((swf->movieSize.xmax - swf->movieSize.xmin)/20); + return (void*)(ptroff_t)((swf->movieSize.xmax - swf->movieSize.xmin)/20); } else if(!strcmp(name, "height")) { - return (void*)((swf->movieSize.ymax - swf->movieSize.ymin)/20); + return (void*)(ptroff_t)((swf->movieSize.ymax - swf->movieSize.ymin)/20); } return 0; } @@ -1555,7 +1659,7 @@ static void swfoutput_setlinewidth(gfxdevice_t*dev, double _linewidth) } -static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, const char*url); +static void drawlink(gfxdevice_t*dev, ActionTAG*,ActionTAG*, gfxline_t*points, char mouseover, char*type, const char*url); static void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points); static void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points); static void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points); @@ -1601,7 +1705,7 @@ void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points) swfoutput_internal*i = (swfoutput_internal*)dev->internal; if(i->shapeid>=0) endshape(dev); - if(i->textid>=0) + if(i->textmode) endtext(dev); /* TODO: escape special characters in url */ @@ -1621,7 +1725,9 @@ void swfoutput_linktourl(gfxdevice_t*dev, const char*url, gfxline_t*points) } actions = action_End(actions); - drawlink(dev, actions, 0, points, 0, url); + drawlink(dev, actions, 0, points, 0, "url", url); + + swf_ActionFree(actions); } void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points) { @@ -1630,7 +1736,7 @@ void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points) if(i->shapeid>=0) endshape(dev); - if(i->textid>=0) + if(i->textmode) endtext(dev); if(!i->config_internallinkfunction || i->config_flashversion>=9) { @@ -1647,7 +1753,9 @@ void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points) char name[80]; sprintf(name, "page%d", page); - drawlink(dev, actions, 0, points, 0, name); + drawlink(dev, actions, 0, points, 0, "page", name); + + swf_ActionFree(actions); } /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets @@ -1662,9 +1770,10 @@ void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points) if(i->shapeid>=0) endshape(dev); - if(i->textid>=0) + if(i->textmode) endtext(dev); + char*type = 0; if(!strncmp(tmp, "call:", 5)) { char*x = strchr(&tmp[5], ':'); @@ -1683,6 +1792,7 @@ void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points) } actions2 = action_End(0); mouseover = 0; + type = "call"; } else { @@ -1695,9 +1805,10 @@ void swfoutput_namedlink(gfxdevice_t*dev, char*name, gfxline_t*points) actions2 = action_PushString(actions2, ""); actions2 = action_SetVariable(actions2); actions2 = action_End(actions2); + type = "subtitle"; } - drawlink(dev, actions1, actions2, points, mouseover, name); + drawlink(dev, actions1, actions2, points, mouseover, type, name); swf_ActionFree(actions1); swf_ActionFree(actions2); @@ -1744,7 +1855,7 @@ static void drawgfxline(gfxdevice_t*dev, gfxline_t*line, int fill) } -static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, const char*url) +static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gfxline_t*points, char mouseover, char*type, const char*url) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; RGBA rgb; @@ -1758,6 +1869,11 @@ static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gf int buttonid = getNewID(dev); gfxbbox_t bbox = gfxline_getbbox(points); + if(i->config_linknameurl) { + actions1 = 0; + actions2 = 0; + } + i->hasbuttons = 1; /* shape */ @@ -1841,10 +1957,14 @@ static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gf swf_ButtonPostProcess(i->tag, 1); } } + char buf[80]; + char*buf2 = 0; const char* name = 0; if(i->config_linknameurl) { - name = url; + buf2 = malloc(strlen(type)+strlen(url)+2); + sprintf(buf2, "%s:%s", type, url); + name = buf2; } else { name = buf; sprintf(buf, "button%d", buttonid); @@ -1862,10 +1982,13 @@ static void drawlink(gfxdevice_t*dev, ActionTAG*actions1, ActionTAG*actions2, gf m = i->page_matrix; m.tx = p.x; m.ty = p.y; - swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,name); + swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&m,0,(U8*)name); } else { - swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,name); + swf_ObjectPlace(i->tag, buttonid, getNewDepth(dev),&i->page_matrix,0,(U8*)name); } + + if(buf2) + free(buf2); } @@ -1991,10 +2114,18 @@ int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value) i->config_caplinewidth = atof(value); } else if(!strcmp(name, "linktarget")) { i->config_linktarget = strdup(value); + } else if(!strcmp(name, "invisibletexttofront")) { + i->config_invisibletexttofront = atoi(value); + } else if(!strcmp(name, "noclips")) { + i->config_noclips = atoi(value); } else if(!strcmp(name, "dumpfonts")) { i->config_dumpfonts = atoi(value); } else if(!strcmp(name, "animate")) { i->config_animate = atoi(value); + } else if(!strcmp(name, "linknameurl")) { + i->config_linknameurl = atoi(value); + } else if(!strcmp(name, "showimages")) { + i->config_showimages = atoi(value); } else if(!strcmp(name, "disablelinks")) { i->config_disablelinks = atoi(value); } else if(!strcmp(name, "simpleviewer")) { @@ -2038,7 +2169,7 @@ int swf_setparameter(gfxdevice_t*dev, const char*name, const char*value) printf("linkcolor==7)\n"); + printf("enablezlib switch on zlib compression (also done if flashversion>=6)\n"); printf("bboxvars store the bounding box of the SWF file in actionscript variables\n"); printf("dots Take care to handle dots correctly\n"); printf("reordertags=0/1 (default: 1) perform some tag optimizations\n"); @@ -2130,7 +2261,9 @@ static int add_image(swfoutput_internal*i, gfximage_t*img, int targetwidth, int if(newsizex Scaling %dx%d image to %dx%d", sizex, sizey, newsizex, newsizey); - newpic = swf_ImageScale(mem, sizex, sizey, newsizex, newsizey); + gfximage_t*ni = gfximage_rescale(img, newsizex, newsizey); + newpic = (RGBA*)ni->data; + free(ni); *newwidth = sizex = newsizex; *newheight = sizey = newsizey; mem = newpic; @@ -2232,6 +2365,11 @@ static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxm SHAPE*shape; swf_ShapeNew(&shape); int fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1); + int lsid = 0; + if(i->config_showimages) { + RGBA pink = {255,255,0,255}; + lsid = swf_ShapeAddLineStyle(shape, 20, &pink); + } swf_SetU16(i->tag, myshapeid); SRECT r = gfxline_getSWFbbox(line); r = swf_ClipRect(i->pagebbox, r); @@ -2239,7 +2377,7 @@ static void swf_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxm swf_SetShapeStyles(i->tag,shape); swf_ShapeCountBits(shape,NULL,NULL); swf_SetShapeBits(i->tag,shape); - swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,0,fsid,0); + swf_ShapeSetAll(i->tag,shape,UNDEFINED_COORD,UNDEFINED_COORD,lsid,fsid,0); i->swflastx = i->swflasty = UNDEFINED_COORD; drawgfxline(dev, line, 1); swf_ShapeSetEnd(i->tag); @@ -2283,6 +2421,8 @@ static void drawoutline(gfxdevice_t*dev, gfxline_t*line) static void swf_startclip(gfxdevice_t*dev, gfxline_t*line) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; + if(i->config_noclips) + return; endtext(dev); endshape(dev); @@ -2349,7 +2489,9 @@ static void swf_startclip(gfxdevice_t*dev, gfxline_t*line) static void swf_endclip(gfxdevice_t*dev) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; - if(i->textid>=0) + if(i->config_noclips) + return; + if(i->textmode) endtext(dev); if(i->shapeid>=0) endshape(dev); @@ -2510,11 +2652,11 @@ static void swf_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcol if(has_dots) gfxline_fix_short_edges(line); /* we need to convert the line into a polygon */ - gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit); - gfxline_t*gfxline = gfxpoly_to_gfxline(poly); + gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID); + gfxline_t*gfxline = gfxline_from_gfxpoly(poly); dev->fill(dev, gfxline, color); gfxline_free(gfxline); - gfxpoly_free(poly); + gfxpoly_destroy(poly); return; } @@ -2555,7 +2697,6 @@ static void swf_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color) gfxbbox_t r = gfxline_getbbox(line); int is_outside_page = !is_inside_page(dev, r.xmin, r.ymin) || !is_inside_page(dev, r.xmax, r.ymax); - //if(i->chardatapos && i->chardata[i->chardatapos-1].color.a) { endtext(dev); if(!i->config_ignoredraworder) @@ -2665,13 +2806,13 @@ static void swf_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*grad swf_FreeGradient(swfgradient);free(swfgradient); } -static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id) +static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id, int version) { SWFFONT*swffont = (SWFFONT*)rfx_calloc(sizeof(SWFFONT)); int t; SRECT bounds = {0,0,0,0}; swffont->id = -1; - swffont->version = 2; + swffont->version = version; swffont->name = (U8*)strdup(id); swffont->layout = (SWFLAYOUT*)rfx_calloc(sizeof(SWFLAYOUT)); swffont->layout->ascent = 0; @@ -2685,21 +2826,26 @@ static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id) swffont->glyph2ascii = (U16*)rfx_calloc(sizeof(U16)*swffont->numchars); swffont->glyph = (SWFGLYPH*)rfx_calloc(sizeof(SWFGLYPH)*swffont->numchars); swffont->glyphnames = (char**)rfx_calloc(sizeof(char*)*swffont->numchars); - for(t=0;tmax_unicode;t++) { - swffont->ascii2glyph[t] = font->unicode2glyph[t]; - } + SRECT max = {0,0,0,0}; for(t=0;tnum_glyphs;t++) { drawer_t draw; gfxline_t*line; double advance = 0; - swffont->glyph2ascii[t] = font->glyphs[t].unicode; - if(swffont->glyph2ascii[t] == 0xffff || swffont->glyph2ascii[t] == 0x0000) { + int u = font->glyphs[t].unicode; + int s; + char twice=0; + for(s=0;snum_glyphs;s++) { + if(swffont->glyph2ascii[s]==u) + twice=1; + } + if(u >= 0xe000 || u == 0x0000 || twice) { /* flash 8 flashtype requires unique unicode IDs for each character. We use the Unicode private user area to assign characters, hoping that the font doesn't contain more than 2048 glyphs */ - swffont->glyph2ascii[t] = 0xe000 + (t&0x1fff); + u = 0xe000 + (t&0x1fff); } + swffont->glyph2ascii[t] = u; if(font->glyphs[t].name) { swffont->glyphnames[t] = strdup(font->glyphs[t].name); @@ -2710,10 +2856,18 @@ static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id) swf_Shape01DrawerInit(&draw, 0); line = font->glyphs[t].line; + + const double scale = GLYPH_SCALE; while(line) { FPOINT c,to; - c.x = line->sx * GLYPH_SCALE; c.y = line->sy * GLYPH_SCALE; - to.x = line->x * GLYPH_SCALE; to.y = line->y * GLYPH_SCALE; + c.x = line->sx * scale; c.y = -line->sy * scale; + //to.x = floor(line->x * scale); to.y = floor(-line->y * scale); + to.x = line->x * scale; to.y = -line->y * scale; + + /*if(strstr(swffont->name, "BIRNU") && t==90) { + to.x += 1; + }*/ + if(line->type == gfx_moveTo) { draw.moveTo(&draw, &to); } else if(line->type == gfx_lineTo) { @@ -2742,6 +2896,7 @@ static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id) swf_ExpandRect2(&bounds, &swffont->layout->bounds[t]); } + for(t=0;tnum_glyphs;t++) { SRECT bbox = swffont->layout->bounds[t]; @@ -2769,12 +2924,8 @@ static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id) The baseline is defined as the y-position zero */ - swffont->layout->ascent = -bounds.ymin; - if(swffont->layout->ascent < 0) - swffont->layout->ascent = 0; - swffont->layout->descent = bounds.ymax; - if(swffont->layout->descent < 0) - swffont->layout->descent = 0; + swffont->layout->ascent = bounds.ymin<0?-bounds.ymin:0; + swffont->layout->descent = bounds.ymax>0?bounds.ymax:0; swffont->layout->leading = bounds.ymax - bounds.ymin; /* if the font has proper ascent/descent values (>0) and those define @@ -2785,6 +2936,7 @@ static SWFFONT* gfxfont_to_swffont(gfxfont_t*font, const char* id) if(font->descent*20 > swffont->layout->descent) swffont->layout->descent = font->descent*20; + swf_FontSort(swffont); return swffont; } @@ -2804,7 +2956,7 @@ static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font) l = l->next; } l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t)); - l->swffont = gfxfont_to_swffont(font, font->id); + l->swffont = gfxfont_to_swffont(font, font->id, (i->config_flashversion>=8 && !NO_FONT3)?3:2); l->next = 0; if(last) { last->next = l; @@ -2831,11 +2983,6 @@ static void swf_addfont(gfxdevice_t*dev, gfxfont_t*font) l->swffont->layout->bounds[iii].xmax/20.0, l->swffont->layout->bounds[iii].ymax/20.0 ); - int t; - for(t=0;tswffont->maxascii;t++) { - if(l->swffont->ascii2glyph[t] == iii) - msg(" | - maps to %d",t); - } } } } @@ -2859,6 +3006,56 @@ static void swf_switchfont(gfxdevice_t*dev, const char*fontid) return; } +/* sets the matrix which is to be applied to characters drawn by swfoutput_drawchar() */ +static void setfontscale(gfxdevice_t*dev,double m11,double m12, double m21,double m22,double x, double y, char force) +{ + m11 *= 1024; + m12 *= 1024; + m21 *= 1024; + m22 *= 1024; + swfoutput_internal*i = (swfoutput_internal*)dev->internal; + if(i->lastfontm11 == m11 && + i->lastfontm12 == m12 && + i->lastfontm21 == m21 && + i->lastfontm22 == m22 && !force) + return; + if(i->textmode) + endtext(dev); + + i->lastfontm11 = m11; + i->lastfontm12 = m12; + i->lastfontm21 = m21; + i->lastfontm22 = m22; + + double xsize = sqrt(m11*m11 + m12*m12); + double ysize = sqrt(m21*m21 + m22*m22); + + int extrazoom = 1; + if(i->config_flashversion>=8 && !NO_FONT3) + extrazoom = 20; + + i->current_font_size = (xsize>ysize?xsize:ysize)*extrazoom; + if(i->current_font_size < 1) + i->current_font_size = 1; + + MATRIX m; + swf_GetMatrix(0, &m); + + if(m21 || m12 || fabs(m11+m22)>0.001) { + double ifs = (double)extrazoom/(i->current_font_size); + m.sx = (S32)((m11*ifs)*65536); m.r1 = -(S32)((m21*ifs)*65536); + m.r0 = (S32)((m12*ifs)*65536); m.sy = -(S32)((m22*ifs)*65536); + } + + /* this is the position of the first char to set a new fontmatrix- + we hope that it's close enough to all other characters using the + font, so we use its position as origin for the matrix */ + m.tx = x*20; + m.ty = y*20; + i->fontmatrix = m; +} + + static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t*color, gfxmatrix_t*matrix) { swfoutput_internal*i = (swfoutput_internal*)dev->internal; @@ -2878,11 +3075,9 @@ static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t* if(!i->swffont || !i->swffont->name || strcmp((char*)i->swffont->name,font->id)) // not equal to current font { - /* TODO: remove the need for this (enhance getcharacterbbox so that it can cope - with multiple fonts */ - endtext(dev); swf_switchfont(dev, font->id); // set the current font } + if(!i->swffont) { msg(" swf_drawchar: Font is NULL"); return; @@ -2891,6 +3086,7 @@ static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t* msg(" No character %d in font %s (%d chars)", glyph, FIXNULL((char*)i->swffont->name), i->swffont->numchars); return; } + glyph = i->swffont->glyph2glyph[glyph]; setfontscale(dev, matrix->m00, matrix->m01, matrix->m10, matrix->m11, matrix->tx, matrix->ty, 0); @@ -2898,7 +3094,7 @@ static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t* i->fontmatrix.r0/65536.0 * i->fontmatrix.r1/65536.0; if(fabs(det) < 0.0005) { /* x direction equals y direction- the text is invisible */ - msg(" Not drawing invisible character character %d (det=%f, m=[%f %f;%f %f]\n", glyph, + msg(" Not drawing invisible character %d (det=%f, m=[%f %f;%f %f]\n", glyph, det, i->fontmatrix.sx/65536.0, i->fontmatrix.r1/65536.0, i->fontmatrix.r0/65536.0, i->fontmatrix.sy/65536.0); @@ -2928,13 +3124,22 @@ static void swf_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyph, gfxcolor_t* if(i->shapeid>=0) endshape(dev); - if(i->textid<0) + if(!i->textmode) starttext(dev); msg(" Drawing char %d in font %d at %d,%d in color %02x%02x%02x%02x", glyph, i->swffont->id, x, y, color->r, color->g, color->b, color->a); - putcharacter(dev, i->swffont->id, glyph, x, y, i->current_font_size, *(RGBA*)color); - swf_FontUseGlyph(i->swffont, glyph); + if(color->a == 0 && i->config_invisibletexttofront) { + RGBA color2 = *(RGBA*)color; + if(i->config_flashversion>=8) { + // use "multiply" blend mode + color2.a = color2.r = color2.g = color2.b = 255; + } + i->topchardata = charbuffer_append(i->topchardata, i->swffont, glyph, x, y, i->current_font_size, color2, &i->fontmatrix); + } else { + i->chardata = charbuffer_append(i->chardata, i->swffont, glyph, x, y, i->current_font_size, *(RGBA*)color, &i->fontmatrix); + } + swf_FontUseGlyph(i->swffont, glyph, i->current_font_size); return; }