2 Implements generation of swf files using the rfxswf lib. The routines
3 in this file are called from pdf2swf.
5 This file is part of swftools.
7 Swftools is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Swftools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with swftools; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 #include "../config.h"
33 #include "swfoutput.h"
36 #include "../lib/log.h"
37 #include "../lib/rfxswf.h"
41 int ignoredraworder=0;
44 int storeallcharacters=0;
49 int fontsplinemaxerror=1;
51 static char storefont = 0;
52 static int flag_protected = 0;
54 typedef unsigned char u8;
55 typedef unsigned short int u16;
56 typedef unsigned long int u32;
59 static char* filename = 0;
62 static int currentswfid = 0;
64 static int startdepth = 1;
67 static int shapeid = -1;
68 static int textid = -1;
70 static int fillstyleid;
71 static int linestyleid;
72 static int swflastx=0;
73 static int swflasty=0;
74 static int lastwasfill = 0;
75 static int shapeisempty = 1;
87 char fillstylechanged = 0;
89 static void startshape(struct swfoutput* obj);
90 static void starttext(struct swfoutput* obj);
91 static void endshape();
92 static void endtext();
94 // matrix multiplication. changes p0
95 static void transform (plotxy*p0,struct swfmatrix*m)
98 x = m->m11*p0->x+m->m12*p0->y;
99 y = m->m21*p0->x+m->m22*p0->y;
104 // write a move-to command into the swf
105 static int moveto(TAG*tag, plotxy p0)
107 int rx = (int)(p0.x*20);
108 int ry = (int)(p0.y*20);
109 if(rx!=swflastx || ry!=swflasty || fillstylechanged) {
110 swf_ShapeSetMove (tag, shape, rx,ry);
111 fillstylechanged = 0;
119 // write a line-to command into the swf
120 static void lineto(TAG*tag, plotxy p0)
122 int px = (int)(p0.x*20);
123 int py = (int)(p0.y*20);
124 int rx = (px-swflastx);
125 int ry = (py-swflasty);
126 /* we can't skip this for rx=0,ry=0, those
128 swf_ShapeSetLine (tag, shape, rx,ry);
130 //swf_ExpandRect3(boundingBox, px, py, linewidth);
131 //swf_ExpandRect3(boundingBox, swflastx, swflasty, linewidth);
138 // write a spline-to command into the swf
139 static void splineto(TAG*tag, plotxy control,plotxy end)
141 int cx = ((int)(control.x*20)-swflastx);
142 int cy = ((int)(control.y*20)-swflasty);
145 int ex = ((int)(end.x*20)-swflastx);
146 int ey = ((int)(end.y*20)-swflasty);
149 if(cx || cy || ex || ey)
150 swf_ShapeSetCurve(tag, shape, cx,cy,ex,ey);
154 /* write a line, given two points and the transformation
156 static void line(TAG*tag, plotxy p0, plotxy p1, struct swfmatrix*m)
164 /* write a cubic (!) spline. This involves calling the approximate()
165 function out of spline.cc to convert it to a quadratic spline. */
166 static void spline(TAG*tag,plotxy p0,plotxy p1,plotxy p2,plotxy p3,struct swfmatrix*m)
169 struct qspline q[128];
183 /* fonts use a different approximation than shapes */
184 num = cspline_approximate(&c, q, fontsplinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
185 //num = cspline_approximate(&c, q, 10.0, APPROXIMATE_INFLECTION);
187 num = cspline_approximate(&c, q, splinemaxerror/20.0, APPROXIMATE_RECURSIVE_BINARY);
191 moveto(tag,q[t].start);
192 splineto(tag,q[t].control, q[t].end);
202 static void stopFill()
206 swf_ShapeSetStyle(tag,shape,linestyleid,0x8000,0);
207 fillstylechanged = 1;
211 static void startFill()
215 swf_ShapeSetStyle(tag,shape,0x8000,fillstyleid,0);
216 fillstylechanged = 1;
221 /* draw an outline. These are generated by pdf2swf and by t1lib
222 (representing characters). */
223 void drawpath(struct swfoutput*output, SWF_OUTLINE*outline, struct swfmatrix*m, int log)
225 if( tag->id != ST_DEFINESHAPE &&
226 tag->id != ST_DEFINESHAPE2 &&
227 tag->id != ST_DEFINESHAPE3)
229 msg("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
233 double lastx=0,lasty=0;
234 double firstx=0,firsty=0;
239 x += (outline->dest.x/(float)0xffff);
240 y += (outline->dest.y/(float)0xffff);
241 if(outline->type == SWF_PATHTYPE_MOVE)
243 if(!init && fill && output->drawmode != DRAWMODE_EOFILL && !ignoredraworder) {
244 /* drawmode=FILL (not EOFILL) means that
245 seperate shapes do not cancel each other out.
246 On SWF side, we need to start a new shape for each
247 closed polygon, because SWF only knows EOFILL.
254 if(((int)(lastx*20) != (int)(firstx*20) ||
255 (int)(lasty*20) != (int)(firsty*20)) &&
264 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
265 line(tag, p0, p1, m);
271 else if(outline->type == SWF_PATHTYPE_LINE)
279 if(log) printf("line: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
282 else if(outline->type == SWF_PATHTYPE_BEZIER)
288 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
291 p1.x=o2->C.x/(float)0xffff+lastx;
292 p1.y=o2->C.y/(float)0xffff+lasty;
293 p2.x=o2->B.x/(float)0xffff+lastx;
294 p2.y=o2->B.y/(float)0xffff+lasty;
297 if(log) printf("spline: %f,%f -> %f,%f\n",p3.x,p3.y,p0.x,p0.y);
298 spline(tag,p0,p1,p2,p3,m);
301 msg("<error> drawpath: unknown outline type:%d\n", outline->type);
305 outline = outline->link;
307 if(((int)(lastx*20) != (int)(firstx*20) ||
308 (int)(lasty*20) != (int)(firsty*20)) &&
317 if(log) printf("fix: %f,%f -> %f,%f\n",p0.x,p0.y,p1.x,p1.y);
318 line(tag, p0, p1, m);
322 plotxy getPivot(SWF_OUTLINE*outline, int dir, double line_width, int end, int trytwo)
324 SWF_PATHPOINT next, next2;
325 double xv=0,yv=0, xv2=0, yv2=0;
330 if(outline->type == SWF_PATHTYPE_LINE) {
331 next = outline->dest;
333 next = ((SWF_BEZIERSEGMENT*)outline)->B;
334 if(next.x==0 && next.y==0) {
335 next = ((SWF_BEZIERSEGMENT*)outline)->C;
337 if(next.x==0 && next.y==0) {
338 next = ((SWF_BEZIERSEGMENT*)outline)->dest;
342 if(trytwo && outline->last && outline->last->type != SWF_PATHTYPE_MOVE) {
343 if(outline->type == SWF_PATHTYPE_LINE) {
344 next2 = outline->last->dest;
346 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)(outline->last))->C;
347 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)(outline->last))->B;
348 next2.x = outline->last->dest.x - c.x;
349 next2.y = outline->last->dest.y - c.y;
350 if(next2.x==0 && next2.y==0) {
351 next2.x = outline->last->dest.x - b.x;
352 next2.y = outline->last->dest.y - b.y;
354 if(next2.x==0 && next2.y==0) {
355 next2.x = outline->last->dest.x;
356 next2.y = outline->last->dest.y;
362 if(outline->type == SWF_PATHTYPE_LINE) {
363 next = outline->dest;
365 SWF_PATHPOINT c = ((SWF_BEZIERSEGMENT*)outline)->C;
366 SWF_PATHPOINT b = ((SWF_BEZIERSEGMENT*)outline)->B;
367 next.x = outline->dest.x - c.x;
368 next.y = outline->dest.y - c.y;
369 if(next.x==0 && next.y==0) {
370 next.x = outline->dest.x - b.x;
371 next.y = outline->dest.y - b.y;
373 if(next.x==0 && next.y==0) {
374 next.x = outline->dest.x;
375 next.y = outline->dest.y;
379 if(trytwo && outline->link && outline->link->type != SWF_PATHTYPE_MOVE) {
380 if(outline->type == SWF_PATHTYPE_LINE) {
381 next2 = outline->link->dest;
383 next2 = ((SWF_BEZIERSEGMENT*)(outline->link))->B;
384 if(next2.x==0 && next2.y==0) {
385 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->C;
387 if(next2.x==0 && next2.y==0) {
388 next2 = ((SWF_BEZIERSEGMENT*)outline->link)->dest;
396 xv = next.y/(float)0xffff;
397 yv = -next.x/(float)0xffff;
399 xv = -next.y/(float)0xffff;
400 yv = next.x/(float)0xffff;
403 double r = (line_width/2)/sqrt(xv*xv+yv*yv);
409 xv2 = next2.y/(float)0xffff;
410 yv2 = -next2.x/(float)0xffff;
412 xv2 = -next2.y/(float)0xffff;
413 yv2 = next2.x/(float)0xffff;
416 double r2 = (line_width/2)/sqrt(xv2*xv2+yv2*yv2);
421 double r3 = (line_width/2)/sqrt(xv*xv+yv*yv);
431 void drawShortPath(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline)
433 double lastx=x, lasty=y;
434 while (outline && outline->type != SWF_PATHTYPE_MOVE)
436 x += (outline->dest.x/(float)0xffff);
437 y += (outline->dest.y/(float)0xffff);
439 if(outline->type == SWF_PATHTYPE_LINE)
446 line(tag, p0, p1, m);
448 else if(outline->type == SWF_PATHTYPE_BEZIER)
451 SWF_BEZIERSEGMENT*o2 = (SWF_BEZIERSEGMENT*)outline;
454 p1.x=o2->C.x/(float)0xffff+lastx;
455 p1.y=o2->C.y/(float)0xffff+lasty;
456 p2.x=o2->B.x/(float)0xffff+lastx;
457 p2.y=o2->B.y/(float)0xffff+lasty;
460 spline(tag,p0,p1,p2,p3,m);
464 outline = outline->link;
468 void drawShortPathWithEnds(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
473 if(line_cap == LINE_CAP_BUTT || line_cap == LINE_CAP_SQUARE) {
476 SWF_OUTLINE *last, *tmp=outline;
477 plotxy s,e,p0,p1,p2,p3,m0,m1,m2,m3;
483 while(tmp && tmp->type != SWF_PATHTYPE_MOVE) {
485 lx += (tmp->dest.x/(float)0xffff);
486 ly += (tmp->dest.y/(float)0xffff);
489 s = getPivot(outline, 0, line_width, 0, 0);
490 e = getPivot(last, 0, line_width, 1, 0);
492 if(line_cap == LINE_CAP_BUTT) {
493 /* make the clipping rectangle slighly bigger
494 than the line ending, so that it get's clipped
504 p2.x = x2 - s.y - s.x*ee;
505 p2.y = y2 + s.x - s.y*ee;
506 p3.x = x2 - s.y + s.x*ee;
507 p3.y = y2 + s.x + s.y*ee;
512 m2.x = lx + e.y - e.x*ee;
513 m2.y = ly - e.x - e.y*ee;
514 m3.x = lx + e.y + e.x*ee;
515 m3.y = ly - e.x + e.y*ee;
517 for(nr=0;nr<2;nr++) {
519 struct plotxy q0,q1,q2,q3,q4,q5;
521 if(line_cap == LINE_CAP_BUTT) {
524 q1.x = sizex; q1.y = 0;
525 q2.x = sizex; q2.y = sizey;
526 q3.x = 0; q3.y = sizey;
528 q0.x = sizex; q0.y = sizey;
529 q1.x = 0; q1.y = sizey;
531 q3.x = sizex; q3.y = 0;
545 line(tag, p0, p1, m);
546 line(tag, p1, p2, m);
547 line(tag, p2, p3, m);
548 line(tag, p3, p0, m);
550 if(line_cap == LINE_CAP_BUTT) {
552 swf_ShapeSetEnd(tag);
553 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
554 swf_ObjectPlaceClip(tag,shapeid,depth,NULL,NULL,NULL,depth+2-nr);
568 drawShortPath(output,x,y,m,outline);
570 if(line_cap == LINE_CAP_BUTT) {
576 void drawT1toRect(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
578 plotxy d1,d2,p1,p2,p3,p4;
580 d1.x = (outline->dest.x/(float)0xffff);
581 d1.y = (outline->dest.y/(float)0xffff);
582 d2 = getPivot(outline, 0, line_width, 0, 0);
584 assert(line_cap != LINE_CAP_ROUND);
585 if(line_cap == LINE_CAP_SQUARE) {
594 p2.x = x + d2.x + d1.x;
595 p2.y = y + d2.y + d1.y;
596 p3.x = x - d2.x + d1.x;
597 p3.y = y - d2.y + d1.y;
607 void drawShortPathWithStraightEnds(struct swfoutput*output, double x, double y, struct swfmatrix* m, SWF_OUTLINE*outline, int num, int line_cap, int line_join, double line_width)
609 SWF_OUTLINE*tmp=outline;
615 drawT1toRect(output, x, y, m,outline, num, line_cap, line_join, line_width);
617 while(tmp->link && tmp->link->type!=SWF_PATHTYPE_MOVE) {
618 xx += (tmp->dest.x/(float)0xffff);
619 yy += (tmp->dest.y/(float)0xffff);
623 assert(tmp->type == SWF_PATHTYPE_LINE);
624 assert(outline->type == SWF_PATHTYPE_LINE);
628 if(outline->link == tmp) {
629 /* the two straight line segments (which are everything we
630 need to draw) are very likely to overlap. To avoid that
631 they cancel each other out at the end points, start a new
632 shape for the second one */
633 endshape();startshape(output);
637 drawT1toRect(output, xx, yy, m, tmp, num, line_cap, line_join, line_width);
639 if(outline->link != tmp)
643 tmp->type = SWF_PATHTYPE_MOVE;
644 x += (outline->dest.x/(float)0xffff);
645 y += (outline->dest.y/(float)0xffff);
646 outline = outline->link;
647 drawShortPath(output, x, y, m, outline);
655 static int t1len(SWF_OUTLINE*line)
658 while(line && line->type != SWF_PATHTYPE_MOVE) {
665 static float t1linelen(SWF_OUTLINE*line)
668 x = (line->dest.x/(float)0xffff);
669 y = (line->dest.y/(float)0xffff);
670 return sqrt(x*x+y*y);
673 void drawpath2poly(struct swfoutput *output, SWF_OUTLINE*outline, struct swfmatrix*m, int log, int line_join, int line_cap, double line_width, double miter_limit)
675 if( tag->id != ST_DEFINESHAPE &&
676 tag->id != ST_DEFINESHAPE2 &&
677 tag->id != ST_DEFINESHAPE3) {
678 msg("<error> internal error: drawpath needs a shape tag, not %d\n",tag->id);
683 double lastx=0,lasty=0;
686 SWF_OUTLINE*tmp = outline, *last = 0;
691 x += (tmp->dest.x/(float)0xffff);
692 y += (tmp->dest.y/(float)0xffff);
694 if(!tmp || tmp->type == SWF_PATHTYPE_MOVE) {
696 if(last->type == SWF_PATHTYPE_LINE && t1linelen(last)>line_width*2 &&
697 lastwasline && line_cap != LINE_CAP_ROUND)
698 drawShortPathWithStraightEnds(output, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
700 drawShortPathWithEnds(output, lastx, lasty, m, last, valid, line_cap, line_join, line_width);
714 if(tmp && tmp->type == SWF_PATHTYPE_LINE && t1linelen(tmp)>line_width*2)
720 tmp->link->last = tmp; // make sure list is properly linked in both directions
725 static inline int colorcompare(RGBA*a,RGBA*b)
737 static const int CHARDATAMAX = 8192;
745 } chardata[CHARDATAMAX];
748 static void putcharacters(TAG*tag)
753 color.r = chardata[0].color.r^255;
762 int charadvance[128];
765 int glyphbits=1; //TODO: can this be zero?
768 if(tag->id != ST_DEFINETEXT &&
769 tag->id != ST_DEFINETEXT2) {
770 msg("<error> internal error: putcharacters needs an text tag, not %d\n",tag->id);
774 msg("<warning> putcharacters called with zero characters");
777 for(pass = 0; pass < 2; pass++)
787 advancebits++; // add sign bit
788 swf_SetU8(tag, glyphbits);
789 swf_SetU8(tag, advancebits);
792 for(t=0;t<=chardatapos;t++)
794 if(lastfontid != chardata[t].fontid ||
795 lastx!=chardata[t].x ||
796 lasty!=chardata[t].y ||
797 !colorcompare(&color, &chardata[t].color) ||
799 lastsize != chardata[t].size ||
802 if(charstorepos && pass==0)
805 for(s=0;s<charstorepos;s++)
807 while(charids[s]>=(1<<glyphbits))
809 while(charadvance[s]>=(1<<advancebits))
813 if(charstorepos && pass==1)
815 tag->writeBit = 0; // Q&D
816 swf_SetBits(tag, 0, 1); // GLYPH Record
817 swf_SetBits(tag, charstorepos, 7); // number of glyphs
819 for(s=0;s<charstorepos;s++)
821 swf_SetBits(tag, charids[s], glyphbits);
822 swf_SetBits(tag, charadvance[s], advancebits);
827 if(pass == 1 && t<chardatapos)
833 if(lastx != chardata[t].x ||
834 lasty != chardata[t].y)
836 newx = chardata[t].x;
837 newy = chardata[t].y;
843 if(!colorcompare(&color, &chardata[t].color))
845 color = chardata[t].color;
848 font.id = chardata[t].fontid;
849 if(lastfontid != chardata[t].fontid || lastsize != chardata[t].size)
852 tag->writeBit = 0; // Q&D
853 swf_TextSetInfoRecord(tag, newfont, chardata[t].size, newcolor, newx,newy);
856 lastfontid = chardata[t].fontid;
857 lastx = chardata[t].x;
858 lasty = chardata[t].y;
859 lastsize = chardata[t].size;
866 int nextt = t==chardatapos-1?t:t+1;
867 int rel = chardata[nextt].x-chardata[t].x;
868 if(rel>=0 && (rel<(1<<(advancebits-1)) || pass==0)) {
870 lastx=chardata[nextt].x;
876 charids[charstorepos] = chardata[t].charid;
877 charadvance[charstorepos] = advance;
884 static void putcharacter(struct swfoutput*obj, int fontid, int charid,
885 int x,int y, int size)
887 if(chardatapos == CHARDATAMAX)
889 msg("<warning> Character buffer too small. SWF will be slightly bigger");
893 chardata[chardatapos].fontid = fontid;
894 chardata[chardatapos].charid = charid;
895 chardata[chardatapos].x = x;
896 chardata[chardatapos].y = y;
897 chardata[chardatapos].color = obj->fillrgb;
898 chardata[chardatapos].size = size;
908 /* process a character. */
909 static int drawchar(struct swfoutput*obj, SWFFONT *swffont, char*character, int charnr, int u, swfmatrix*m)
912 if(m->m12!=0 || m->m21!=0)
918 msg("<warning> Font is NULL");
923 msg("<verbose> Non diagonal font matrix: %f %f", m->m11, m->m21);
924 msg("<verbose> | %f %f", m->m12, m->m22);
927 //if(usefonts && ! drawonlyshapes)
930 int charid = getCharID(swffont, charnr, character, u);
933 msg("<warning> Didn't find character '%s' (c=%d,u=%d) in current charset (%s, %d characters)",
934 FIXNULL(character),charnr, u, FIXNULL((char*)swffont->name), swffont->numchars);
935 /*fontlist_t*it = fontlist;
937 msg("<warning> Font history: %s [%d]", it->swffont->name, getCharID(it->swffont, charnr, character, u));
948 putcharacter(obj, swffont->id, charid,(int)(m->m13*20),(int)(m->m23*20),
950 swf_FontUseGlyph(swffont, charid);
955 SWF_OUTLINE*outline = font->getOutline(character, charnr);
956 char* charname = character;
959 msg("<warning> Didn't find character '%s' (%d) in current charset (%s)",
960 FIXNULL(character),charnr,FIXNULL(font->getName()));
979 drawpath(tag, outline, &m2, 0);
984 /* draw a curved polygon. */
985 void swfoutput_drawpath(swfoutput*output, SWF_OUTLINE*outline,
991 /* Multiple polygons in one shape don't overlap correctly,
992 so we better start a new shape here if the polygon is filled
994 if(shapeid>=0 && fill && !ignoredraworder) {
1006 drawpath(output, outline,m, 0);
1009 void swfoutput_drawpath2poly(struct swfoutput*output, SWF_OUTLINE*outline, struct swfmatrix*m, int line_join, int line_cap, double line_width, double miter_limit)
1019 drawpath2poly(output, outline, m, 0, line_join, line_cap, line_width, miter_limit);
1022 int getCharID(SWFFONT *font, int charnr, char *charname, int u)
1026 for(t=0;t<font->numchars;t++) {
1027 if(font->glyphnames[t] && !strcmp(font->glyphnames[t],charname)) {
1031 /* if we didn't find the character, maybe
1032 we can find the capitalized version */
1033 for(t=0;t<font->numchars;t++) {
1034 if(font->glyphnames[t] && !strcasecmp(font->glyphnames[t],charname)) {
1041 /* try to use the unicode id */
1042 if(u>=0 && u<font->maxascii && font->ascii2glyph[u]>=0) {
1043 return font->ascii2glyph[u];
1047 if(charnr>=0 && charnr<font->numchars) {
1051 /* the following is technically wrong, and only works if the font encoding
1052 is US-ASCII based. It's needed for fonts which return broken unicode
1054 /* if(charnr>=0 && charnr<font->maxascii && font->ascii2glyph[charnr]>=0) {
1055 return font->ascii2glyph[charnr];
1062 /* set's the t1 font index of the font to use for swfoutput_drawchar(). */
1063 void swfoutput_setfont(struct swfoutput*obj, char*fontid, char*filename)
1065 fontlist_t*last=0,*iterator;
1067 msg("<error> No fontid");
1071 if(obj->swffont && obj->swffont->name && !strcmp((char*)obj->swffont->name,fontid))
1074 iterator = fontlist;
1076 if(!strcmp((char*)iterator->swffont->name,fontid)) {
1077 obj->swffont = iterator->swffont;
1081 iterator = iterator->next;
1085 msg("<error> No filename given for font- internal error?");
1089 swf_SetLoadFontParameters(0,/*skip unused*/0,/*full unicode*/1);
1090 SWFFONT*swffont = swf_LoadFont(filename);
1093 msg("<warning> Couldn't load font %s (%s)", fontid, filename);
1094 swffont = swf_LoadFont(0);
1097 swf_FontSetID(swffont, ++currentswfid);
1099 if(screenloglevel >= LOGLEVEL_DEBUG) {
1100 // print font information
1101 msg("<debug> Font %s (%s)",swffont->name, filename);
1102 msg("<debug> | ID: %d", swffont->id);
1103 msg("<debug> | Version: %d", swffont->version);
1104 msg("<debug> | Name: %s", fontid);
1105 msg("<debug> | Numchars: %d", swffont->numchars);
1106 msg("<debug> | Maxascii: %d", swffont->maxascii);
1107 msg("<debug> | Style: %d", swffont->style);
1108 msg("<debug> | Encoding: %d", swffont->encoding);
1109 for(int iii=0; iii<swffont->numchars;iii++) {
1110 msg("<debug> | Glyph %d) name=%s, unicode=%d size=%d bbox=(%.2f,%.2f,%.2f,%.2f)\n", iii, swffont->glyphnames[iii], swffont->glyph2ascii[iii], swffont->glyph[iii].shape->bitlen,
1111 swffont->layout->bounds[iii].xmin/20.0,
1112 swffont->layout->bounds[iii].ymin/20.0,
1113 swffont->layout->bounds[iii].xmax/20.0,
1114 swffont->layout->bounds[iii].ymax/20.0
1119 /* set the font name to the ID we use here */
1120 if(swffont->name) free(swffont->name);
1121 swffont->name = (U8*)strdup(fontid);
1123 iterator = new fontlist_t;
1124 iterator->swffont = swffont;
1128 last->next = iterator;
1130 fontlist = iterator;
1132 obj->swffont = swffont;
1135 int swfoutput_queryfont(struct swfoutput*obj, char*fontid)
1137 fontlist_t *iterator = fontlist;
1139 if(!strcmp((char*)iterator->swffont->name,fontid))
1141 iterator = iterator->next;
1146 /* set's the matrix which is to be applied to characters drawn by
1147 swfoutput_drawchar() */
1148 void swfoutput_setfontmatrix(struct swfoutput*obj,double m11,double m12,
1149 double m21,double m22)
1151 if(obj->fontm11 == m11 &&
1152 obj->fontm12 == m12 &&
1153 obj->fontm21 == m21 &&
1154 obj->fontm22 == m22)
1164 /* draws a character at x,y. */
1165 int swfoutput_drawchar(struct swfoutput* obj,double x,double y,char*character, int charnr, int u)
1168 m.m11 = obj->fontm11;
1169 m.m12 = obj->fontm12;
1170 m.m21 = obj->fontm21;
1171 m.m22 = obj->fontm22;
1174 return drawchar(obj, obj->swffont, character, charnr, u, &m);
1177 /* initialize the swf writer */
1178 void swfoutput_init(struct swfoutput* obj, char*_filename, int x1, int y1, int x2, int y2)
1182 memset(obj, 0, sizeof(struct swfoutput));
1183 filename = _filename;
1187 msg("<verbose> initializing swf output for size %d*%d\n", sizex,sizey);
1192 memset(&swf,0x00,sizeof(SWF));
1194 swf.fileVersion = flashversion;
1195 swf.frameRate = 0x0040; // 1 frame per 4 seconds
1196 swf.movieSize.xmin = 20*x1;
1197 swf.movieSize.ymin = 20*y1;
1198 swf.movieSize.xmax = 20*x2;
1199 swf.movieSize.ymax = 20*y2;
1203 swf.firstTag = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
1205 rgb.a = rgb.r = rgb.g = rgb.b = 0xff;
1206 swf_SetRGB(tag,&rgb);
1208 if(1)/* add white rectangle */
1213 int shapeid = ++currentswfid;
1218 tag = swf_InsertTag(tag, ST_DEFINESHAPE);
1220 fs1 = swf_ShapeAddSolidFillStyle(s, &rgb);
1221 swf_SetU16(tag,shapeid);
1222 swf_SetRect(tag,&r);
1223 swf_SetShapeHeader(tag,s);
1224 swf_ShapeSetAll(tag,s,x1*20,y1*20,ls1,fs1,0);
1225 swf_ShapeSetLine(tag,s,20*(x2-x1),0);
1226 swf_ShapeSetLine(tag,s,0,20*(y2-y1));
1227 swf_ShapeSetLine(tag,s,20*(x1-x2),0);
1228 swf_ShapeSetLine(tag,s,0,20*(y1-y2));
1229 swf_ShapeSetEnd(tag);
1231 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1232 swf_ObjectPlace(tag,shapeid,depth++,0,0,0);
1233 tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
1234 swf_ObjectPlaceClip(tag,shapeid,depth++,0,0,0,65535);
1238 tag = swf_InsertTag(tag, ST_PROTECT);
1243 void swfoutput_setprotected() //write PROTECT tag
1248 static void startshape(struct swfoutput*obj)
1256 tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1258 swf_ShapeNew(&shape);
1259 linestyleid = swf_ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1260 rgb.r = obj->fillrgb.r;
1261 rgb.g = obj->fillrgb.g;
1262 rgb.b = obj->fillrgb.b;
1263 fillstyleid = swf_ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1265 shapeid = ++currentswfid;
1266 swf_SetU16(tag,shapeid); // ID
1273 swf_SetRect(tag,&r);
1275 swf_SetShapeStyles(tag,shape);
1276 swf_ShapeCountBits(shape,NULL,NULL);
1277 swf_SetShapeBits(tag,shape);
1279 swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,linestyleid,0,0);
1280 swflastx=swflasty=0;
1285 static void starttext(struct swfoutput*obj)
1291 tag = swf_InsertTag(tag,ST_DEFINETEXT);
1292 textid = ++currentswfid;
1293 swf_SetU16(tag, textid);
1300 swf_SetRect(tag,&r);
1309 swf_SetMatrix(tag,&m);
1310 swflastx=swflasty=0;
1313 static void endshape()
1317 swf_ShapeSetEnd(tag);
1320 // delete the tag again, we didn't do anything
1323 swf_DeleteTag(todel);
1325 /* TODO: fix bounding box */
1326 tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1327 swf_ObjectPlace(tag,shapeid,/*depth*/depth++,NULL,NULL,NULL);
1332 static void endtext()
1338 tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1339 swf_ObjectPlace(tag,textid,/*depth*/depth++,NULL,NULL,NULL);
1343 static void endpage(struct swfoutput*obj)
1350 swfoutput_endclip(obj);
1354 atag = action_Stop(atag);
1355 atag = action_End(atag);
1356 tag = swf_InsertTag(tag,ST_DOACTION);
1357 swf_ActionSet(tag,atag);
1359 tag = swf_InsertTag(tag,ST_SHOWFRAME);
1362 void swfoutput_newpage(struct swfoutput*obj)
1366 for(depth--;depth>=startdepth;depth--) {
1367 tag = swf_InsertTag(tag,ST_REMOVEOBJECT2);
1368 swf_SetU16(tag,depth);
1374 /* Perform cleaning up, complete the swf, and write it out. */
1375 void swfoutput_destroy(struct swfoutput* obj)
1378 fontlist_t *tmp,*iterator = fontlist;
1380 TAG*mtag = swf.firstTag;
1381 if(iterator->swffont) {
1382 mtag = swf_InsertTag(mtag, ST_DEFINEFONT2);
1383 /*if(!storeallcharacters)
1384 swf_FontReduce(iterator->swffont);*/
1385 swf_FontSetDefine2(mtag, iterator->swffont);
1386 swf_FontFree(iterator->swffont);
1390 iterator = iterator->next;
1397 fi = open(filename, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY, 0777);
1402 msg("<fatal> Could not create \"%s\". ", FIXNULL(filename));
1406 tag = swf_InsertTag(tag,ST_END);
1408 if(enablezlib || flashversion>=6) {
1409 if FAILED(swf_WriteSWC(fi,&swf))
1410 msg("<error> WriteSWC() failed.\n");
1412 if FAILED(swf_WriteSWF(fi,&swf))
1413 msg("<error> WriteSWF() failed.\n");
1418 msg("<notice> SWF written\n");
1421 void swfoutput_setdrawmode(swfoutput* obj, int mode)
1423 obj->drawmode = mode;
1424 if(mode == DRAWMODE_FILL)
1426 else if(mode == DRAWMODE_EOFILL)
1428 else if(mode == DRAWMODE_STROKE)
1430 else if(mode == DRAWMODE_CLIP)
1432 else if(mode == DRAWMODE_EOCLIP)
1436 void swfoutput_setfillcolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1438 if(obj->fillrgb.r == r &&
1439 obj->fillrgb.g == g &&
1440 obj->fillrgb.b == b &&
1441 obj->fillrgb.a == a) return;
1451 void swfoutput_setstrokecolor(swfoutput* obj, u8 r, u8 g, u8 b, u8 a)
1453 if(obj->strokergb.r == r &&
1454 obj->strokergb.g == g &&
1455 obj->strokergb.b == b &&
1456 obj->strokergb.a == a) return;
1460 obj->strokergb.r = r;
1461 obj->strokergb.g = g;
1462 obj->strokergb.b = b;
1463 obj->strokergb.a = a;
1466 void swfoutput_setlinewidth(struct swfoutput*obj, double linewidth)
1468 if(obj->linewidth == (u16)(linewidth*20))
1473 obj->linewidth = (u16)(linewidth*20);
1477 void swfoutput_startclip(swfoutput*obj, SWF_OUTLINE*outline, struct swfmatrix*m)
1486 msg("<warning> Too many clip levels.");
1491 int olddrawmode = obj->drawmode;
1492 swfoutput_setdrawmode(obj, DRAWMODE_CLIP);
1493 swfoutput_drawpath(obj, outline, m);
1494 swf_ShapeSetEnd(tag);
1495 swfoutput_setdrawmode(obj, olddrawmode);
1497 tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1498 cliptags[clippos] = tag;
1499 clipshapes[clippos] = shapeid;
1500 clipdepths[clippos] = depth++;
1505 void swfoutput_endclip(swfoutput*obj)
1513 msg("<error> Invalid end of clipping region");
1517 swf_ObjectPlaceClip(cliptags[clippos],clipshapes[clippos],clipdepths[clippos],NULL,NULL,NULL,depth++);
1520 static void drawlink(struct swfoutput*obj, ActionTAG*,ActionTAG*, swfcoord*points, char mouseover);
1522 void swfoutput_linktourl(struct swfoutput*obj, char*url, swfcoord*points)
1525 if(!strncmp("http://pdf2swf:", url, 15)) {
1526 char*tmp = strdup(url);
1527 int l = strlen(tmp);
1530 swfoutput_namedlink(obj, tmp+15, points);
1541 actions = action_GetUrl(0, url, "_parent");
1543 actions = action_GetUrl(0, url, "_this");
1544 actions = action_End(actions);
1546 drawlink(obj, actions, 0, points,0);
1548 void swfoutput_linktopage(struct swfoutput*obj, int page, swfcoord*points)
1557 actions = action_GotoFrame(0, page);
1558 actions = action_End(actions);
1560 drawlink(obj, actions, 0, points,0);
1563 /* Named Links (a.k.a. Acrobatmenu) are used to implement various gadgets
1564 of the viewer objects, like subtitles, index elements etc.
1566 void swfoutput_namedlink(struct swfoutput*obj, char*name, swfcoord*points)
1568 ActionTAG *actions1,*actions2;
1569 char*tmp = strdup(name);
1577 if(!strncmp(tmp, "call:", 5))
1579 char*x = strchr(&tmp[5], ':');
1581 actions1 = action_PushInt(0, 0); //number of parameters (0)
1582 actions1 = action_PushString(actions1, &tmp[5]); //function name
1583 actions1 = action_CallFunction(actions1);
1586 actions1 = action_PushString(0, x+1); //parameter
1587 actions1 = action_PushInt(actions1, 1); //number of parameters (1)
1588 actions1 = action_PushString(actions1, &tmp[5]); //function name
1589 actions1 = action_CallFunction(actions1);
1591 actions2 = action_End(0);
1596 actions1 = action_PushString(0, "/:subtitle");
1597 actions1 = action_PushString(actions1, name);
1598 actions1 = action_SetVariable(actions1);
1599 actions1 = action_End(actions1);
1601 actions2 = action_PushString(0, "/:subtitle");
1602 actions2 = action_PushString(actions2, "");
1603 actions2 = action_SetVariable(actions2);
1604 actions2 = action_End(actions2);
1607 drawlink(obj, actions1, actions2, points,mouseover);
1609 swf_ActionFree(actions1);
1610 swf_ActionFree(actions2);
1614 static void drawlink(struct swfoutput*obj, ActionTAG*actions1, ActionTAG*actions2, swfcoord*points, char mouseover)
1620 struct plotxy p1,p2,p3,p4;
1624 double xmax=xmin=points[0].x,ymax=ymin=points[0].y;
1628 int buttonid = ++currentswfid;
1631 if(points[t].x>xmax) xmax=points[t].x;
1632 if(points[t].y>ymax) ymax=points[t].y;
1633 if(points[t].x<xmin) xmin=points[t].x;
1634 if(points[t].y<ymin) ymin=points[t].y;
1637 p1.x=points[0].x; p1.y=points[0].y; p2.x=points[1].x; p2.y=points[1].y;
1638 p3.x=points[2].x; p3.y=points[2].y; p4.x=points[3].x; p4.y=points[3].y;
1640 /* the following code subtracts the upper left edge from all coordinates,
1641 and set's posx,posy so that ST_PLACEOBJECT is used with a matrix.
1642 Necessary for preprocessing with swfcombine. */
1643 posx = xmin; posy = ymin;
1644 p1.x-=posx;p2.x-=posx;p3.x-=posx;p4.x-=posx;
1645 p1.y-=posy;p2.y-=posy;p3.y-=posy;p4.y-=posy;
1646 xmin -= posx; ymin -= posy;
1647 xmax -= posx; ymax -= posy;
1650 myshapeid = ++currentswfid;
1651 tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1652 swf_ShapeNew(&shape);
1653 rgb.r = rgb.b = rgb.a = rgb.g = 0;
1654 fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1655 swf_SetU16(tag, myshapeid);
1656 r.xmin = (int)(xmin*20);
1657 r.ymin = (int)(ymin*20);
1658 r.xmax = (int)(xmax*20);
1659 r.ymax = (int)(ymax*20);
1660 swf_SetRect(tag,&r);
1661 swf_SetShapeStyles(tag,shape);
1662 swf_ShapeCountBits(shape,NULL,NULL);
1663 swf_SetShapeBits(tag,shape);
1664 swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1665 swflastx = swflasty = 0;
1671 swf_ShapeSetEnd(tag);
1674 myshapeid2 = ++currentswfid;
1675 tag = swf_InsertTag(tag,ST_DEFINESHAPE3);
1676 swf_ShapeNew(&shape);
1677 rgb.r = rgb.b = rgb.a = rgb.g = 255;
1679 fsid = swf_ShapeAddSolidFillStyle(shape,&rgb);
1680 swf_SetU16(tag, myshapeid2);
1681 r.xmin = (int)(xmin*20);
1682 r.ymin = (int)(ymin*20);
1683 r.xmax = (int)(xmax*20);
1684 r.ymax = (int)(ymax*20);
1685 swf_SetRect(tag,&r);
1686 swf_SetShapeStyles(tag,shape);
1687 swf_ShapeCountBits(shape,NULL,NULL);
1688 swf_SetShapeBits(tag,shape);
1689 swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,0,fsid,0);
1690 swflastx = swflasty = 0;
1696 swf_ShapeSetEnd(tag);
1700 tag = swf_InsertTag(tag,ST_DEFINEBUTTON);
1701 swf_SetU16(tag,buttonid); //id
1702 swf_ButtonSetFlags(tag, 0); //menu=no
1703 swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1704 swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1705 swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1706 swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1708 swf_ActionSet(tag,actions1);
1713 tag = swf_InsertTag(tag,ST_DEFINEBUTTON2);
1714 swf_SetU16(tag,buttonid); //id
1715 swf_ButtonSetFlags(tag, 0); //menu=no
1716 swf_ButtonSetRecord(tag,0x01,myshapeid,depth,0,0);
1717 swf_ButtonSetRecord(tag,0x02,myshapeid2,depth,0,0);
1718 swf_ButtonSetRecord(tag,0x04,myshapeid2,depth,0,0);
1719 swf_ButtonSetRecord(tag,0x08,myshapeid,depth,0,0);
1720 swf_SetU8(tag,0); // end of button records
1721 swf_ButtonSetCondition(tag, BC_IDLE_OVERUP);
1722 swf_ActionSet(tag,actions1);
1724 swf_ButtonSetCondition(tag, BC_OVERUP_IDLE);
1725 swf_ActionSet(tag,actions2);
1727 swf_ButtonPostProcess(tag, 2);
1730 swf_ButtonPostProcess(tag, 1);
1734 tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1736 if(posx!=0 || posy!=0) {
1738 swf_GetMatrix(0,&m);
1739 m.tx = (int)(posx*20);
1740 m.ty = (int)(posy*20);
1741 swf_ObjectPlace(tag, buttonid, depth++,&m,0,0);
1744 swf_ObjectPlace(tag, buttonid, depth++,0,0,0);
1748 static void drawimage(struct swfoutput*obj, int bitid, int sizex,int sizey,
1749 double x1,double y1,
1750 double x2,double y2,
1751 double x3,double y3,
1752 double x4,double y4)
1758 struct plotxy p1,p2,p3,p4;
1760 double xmax=x1,ymax=y1,xmin=x1,ymin=y1;
1761 if(x2>xmax) xmax=x2;
1762 if(y2>ymax) ymax=y2;
1763 if(x2<xmin) xmin=x2;
1764 if(y2<ymin) ymin=y2;
1765 if(x3>xmax) xmax=x3;
1766 if(y3>ymax) ymax=y3;
1767 if(x3<xmin) xmin=x3;
1768 if(y3<ymin) ymin=y3;
1769 if(x4>xmax) xmax=x4;
1770 if(y4>ymax) ymax=y4;
1771 if(x4<xmin) xmin=x4;
1772 if(y4<ymin) ymin=y4;
1778 {p1.x = (int)(p1.x*20)/20.0;
1779 p1.y = (int)(p1.y*20)/20.0;
1780 p2.x = (int)(p2.x*20)/20.0;
1781 p2.y = (int)(p2.y*20)/20.0;
1782 p3.x = (int)(p3.x*20)/20.0;
1783 p3.y = (int)(p3.y*20)/20.0;
1784 p4.x = (int)(p4.x*20)/20.0;
1785 p4.y = (int)(p4.y*20)/20.0;}
1788 m.sx = (int)(65536*20*(p4.x-p1.x)/sizex);
1789 m.r1 = -(int)(65536*20*(p4.y-p1.y)/sizex);
1790 m.r0 = (int)(65536*20*(p1.x-p2.x)/sizey);
1791 m.sy = -(int)(65536*20*(p1.y-p2.y)/sizey);
1793 m.tx = (int)(p1.x*20);
1794 m.ty = (int)(p1.y*20);
1797 myshapeid = ++currentswfid;
1798 tag = swf_InsertTag(tag,ST_DEFINESHAPE);
1799 swf_ShapeNew(&shape);
1800 //lsid = ShapeAddLineStyle(shape,obj->linewidth,&obj->strokergb);
1801 //fsid = ShapeAddSolidFillStyle(shape,&obj->fillrgb);
1802 fsid = swf_ShapeAddBitmapFillStyle(shape,&m,bitid,1);
1803 swf_SetU16(tag, myshapeid);
1804 r.xmin = (int)(xmin*20);
1805 r.ymin = (int)(ymin*20);
1806 r.xmax = (int)(xmax*20);
1807 r.ymax = (int)(ymax*20);
1808 swf_SetRect(tag,&r);
1809 swf_SetShapeStyles(tag,shape);
1810 swf_ShapeCountBits(shape,NULL,NULL);
1811 swf_SetShapeBits(tag,shape);
1812 swf_ShapeSetAll(tag,shape,/*x*/0,/*y*/0,lsid,fsid,0);
1813 swflastx = swflasty = 0;
1820 ShapeMoveTo (tag, shape, (int)(x1*20),(int)(y1*20));
1821 ShapeSetLine (tag, shape, (int)(x1*20);
1822 ShapeSetLine (tag, shape, x*20,0);
1823 ShapeSetLine (tag, shape, 0,-y*20);
1824 ShapeSetLine (tag, shape, -x*20,0);*/
1825 swf_ShapeSetEnd(tag);
1828 tag = swf_InsertTag(tag,ST_PLACEOBJECT2);
1829 swf_ObjectPlace(tag,myshapeid,/*depth*/depth++,NULL,NULL,NULL);
1832 int swfoutput_drawimagejpeg_old(struct swfoutput*obj, char*filename, int sizex,int sizey,
1833 double x1,double y1,
1834 double x2,double y2,
1835 double x3,double y3,
1836 double x4,double y4)
1844 int bitid = ++currentswfid;
1846 tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1847 swf_SetU16(tag, bitid);
1848 if(swf_SetJPEGBits(tag, filename, jpegquality)<0) {
1854 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1858 int swfoutput_drawimagejpeg(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
1859 double x1,double y1,
1860 double x2,double y2,
1861 double x3,double y3,
1862 double x4,double y4)
1872 int bitid = ++currentswfid;
1874 tag = swf_InsertTag(tag,ST_DEFINEBITSJPEG2);
1875 swf_SetU16(tag, bitid);
1876 swf_SetJPEGBits2(tag,sizex,sizey,mem,jpegquality);
1877 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1881 int swfoutput_drawimagelossless(struct swfoutput*obj, RGBA*mem, int sizex,int sizey,
1882 double x1,double y1,
1883 double x2,double y2,
1884 double x3,double y3,
1885 double x4,double y4)
1893 int bitid = ++currentswfid;
1895 tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS);
1896 swf_SetU16(tag, bitid);
1897 if(swf_SetLosslessBits(tag,sizex,sizey,mem, BMF_32BIT)<0) {
1903 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1907 int swfoutput_drawimagelosslessN(struct swfoutput*obj, U8*mem, RGBA*pal, int sizex,int sizey,
1908 double x1,double y1,
1909 double x2,double y2,
1910 double x3,double y3,
1911 double x4,double y4, int n)
1922 /* SWF expects scanlines to be 4 byte aligned */
1925 mem2 = (U8*)malloc(BYTES_PER_SCANLINE(sizex)*sizey);
1927 for(y=0;y<sizey;y++)
1929 for(x=0;x<sizex;x++)
1930 *ptr++ = mem[y*sizex+x];
1931 ptr+= BYTES_PER_SCANLINE(sizex)-sizex;
1936 int bitid = ++currentswfid;
1938 tag = swf_InsertTag(tag,ST_DEFINEBITSLOSSLESS2);
1939 swf_SetU16(tag, bitid);
1940 if(swf_SetLosslessBitsIndexed(tag,sizex,sizey,mem, pal, n)<0) {
1948 drawimage(obj, bitid, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);
1952 void swfoutput_drawimageagain(struct swfoutput*obj, int id, int sizex,int sizey,
1953 double x1,double y1,
1954 double x2,double y2,
1955 double x3,double y3,
1956 double x4,double y4)
1964 drawimage(obj, id, sizex, sizey, x1,y1,x2,y2,x3,y3,x4,y4);