added swf_WriteFont and swfReadFont routines
[swftools.git] / lib / modules / swftext.c
1 /* swftext.c
2
3    Text and font routines
4       
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This file is distributed under the GPL, see file COPYING for details 
11
12 */
13
14 #define TF_TEXTCONTROL  0x80
15 #define TF_HASFONT      0x08
16 #define TF_HASCOLOR     0x04
17 #define TF_HASYOFFSET   0x02
18 #define TF_HASXOFFSET   0x01
19
20 #define FF_WIDECODES    0x01
21 #define FF_BOLD         0x02
22 #define FF_ITALIC       0x04
23 #define FF_ANSI         0x08
24 #define FF_SHIFTJIS     0x10
25 #define FF_UNICODE      0x20
26
27 static const int WRITEFONTID = 0x4e46; // font id for WriteFont and ReadFont
28
29 int swf_FontEnumerate(SWF * swf,void (*FontCallback) (U16,U8*))
30 { int n;
31   TAG * t;
32   if (!swf) return -1;
33   t = swf->firstTag;
34   n = 0;
35
36   while (t)
37   { if (swf_GetTagID(t)==ST_DEFINEFONTINFO)
38     { n++;
39       if (FontCallback)
40       { U16 id;
41         int l;
42         U8 s[257];
43         swf_SaveTagPos(t);
44         swf_SetTagPos(t,0);
45
46         id  = swf_GetU16(t);
47         l   = swf_GetU8(t);
48         swf_GetBlock(t,s,l);
49         s[l] = 0;
50
51         (FontCallback)(id,s); 
52       
53         swf_RestoreTagPos(t);
54       }
55     }
56     t = swf_NextTag(t);
57   }
58   return n;
59 }
60
61 int swf_FontExtract_DefineFont(int id,SWFFONT * f,TAG * t,SHAPE * * shapes)
62 { U16 fid;
63   swf_SaveTagPos(t);
64   swf_SetTagPos(t,0);
65
66   fid = swf_GetU16(t);
67   if ((!id)||(id==fid))
68   { U16 ofs[MAX_CHAR_PER_FONT];
69     int n,i;
70       
71     id = fid;
72     f->id = fid;
73
74     ofs[0] = swf_GetU16(t);
75     n = ofs[0]/2;
76
77     for (i=1;i<n;i++) if (i<MAX_CHAR_PER_FONT) ofs[i] = swf_GetU16(t); else swf_GetU16(t);
78     for (i=0;i<n;i++) if (i<MAX_CHAR_PER_FONT) swf_GetSimpleShape(t,&shapes[i]);
79     
80   }
81
82   swf_RestoreTagPos(t);
83   return id;
84 }
85
86 int swf_FontExtract_DefineFontInfo(int id,SWFFONT * f,TAG * t,SHAPE * * shapes)
87 { U16 fid;
88   swf_SaveTagPos(t);
89   swf_SetTagPos(t,0);
90
91   fid = swf_GetU16(t);
92   if (fid==id)
93   { U8 l = swf_GetU8(t);
94     int i;
95     
96     if (l)
97     { if (f->name) free(f->name);
98       f->name = (U8*)malloc(l+1);
99       if (f->name)
100       { swf_GetBlock(t,f->name,l);
101         f->name[l] = 0;
102       }
103       else
104       { swf_RestoreTagPos(t);
105         return -1;
106       }
107     }
108     f->flags = swf_GetU8(t);
109
110     i = 0;
111     while (shapes[i])
112     { U16 code = ((f->flags&FF_WIDECODES)?swf_GetU16(t):swf_GetU8(t))%MAX_CHAR_PER_FONT;
113         
114       f->glyph[code].shape = shapes[i];
115       f->glyph[code].gid   = i;
116       if (i<MAX_CHAR_PER_FONT) f->codes[i] = code;
117
118       i++;
119     }
120   }
121
122   swf_RestoreTagPos(t);
123   return id;
124 }
125
126 #define FEDTJ_PRINT  0x01
127 #define FEDTJ_MODIFY 0x02
128
129 int swf_FontExtract_DefineText(int id,SWFFONT * f,TAG * t,int jobs)
130 { U16    cid;
131   SRECT  r;
132   MATRIX m;
133   U8     gbits, abits, flags;
134   int    fid;
135
136   fid = 0;
137
138   swf_SaveTagPos(t);
139   swf_SetTagPos(t,0);
140
141   cid = swf_GetU16(t);
142   swf_GetRect(t,&r);
143   swf_GetMatrix(t,&m);
144   gbits = swf_GetU8(t);
145   abits = swf_GetU8(t);
146
147   flags = swf_GetU8(t);
148   
149   while(flags)
150   { if (flags&TF_TEXTCONTROL)
151     { if (flags&TF_HASFONT) fid = swf_GetU16(t);
152       if (flags&TF_HASCOLOR)
153       { swf_GetU8(t); // rgb
154         swf_GetU8(t);
155         swf_GetU8(t);
156         if (swf_GetTagID(t)==ST_DEFINETEXT2) swf_GetU8(t);
157       }
158       if (flags&TF_HASXOFFSET) swf_GetS16(t);
159       if (flags&TF_HASYOFFSET) swf_GetS16(t);
160       if (flags&TF_HASFONT) swf_GetU16(t);
161     }
162     else
163     { int i;
164       for (i=0;i<flags;i++)
165       { int glyph;
166         int adv;
167         glyph = swf_GetBits(t,gbits);
168         adv = swf_GetBits(t,abits);
169         if (id==fid)                    // mitlesen ?
170         { int code = f->codes[glyph];
171           if (jobs&FEDTJ_PRINT) printf("%c",code);
172           if (jobs&FEDTJ_MODIFY)
173             /*if (f->glyph[code].advance)*/ f->glyph[code].advance = adv;
174         }
175       }
176       if ((id==fid)&&(jobs&FEDTJ_PRINT)) printf("\n");
177     }
178     flags = swf_GetU8(t);
179   }
180   
181   swf_RestoreTagPos(t);
182   return id;
183 }  
184
185 int swf_FontExtract(SWF * swf,int id,SWFFONT * * font)
186 { TAG * t;
187   SWFFONT * f;
188   SHAPE * shapes[MAX_CHAR_PER_FONT];
189     
190   if ((!swf)||(!font)) return -1;
191
192   f = (SWFFONT *)malloc(sizeof(SWFFONT)); font[0] = f;
193   if (!f) return -1;
194   
195   memset(shapes,0x00,sizeof(shapes));
196   memset(f,0x00,sizeof(SWFFONT));
197
198   t = swf->firstTag;
199
200   while (t)
201   { int nid = 0;
202     switch (swf_GetTagID(t))
203     { case ST_DEFINEFONT:
204         nid = swf_FontExtract_DefineFont(id,f,t,shapes);
205         break;
206         
207       case ST_DEFINEFONTINFO:
208         nid = swf_FontExtract_DefineFontInfo(id,f,t,shapes);
209         break;
210         
211       case ST_DEFINETEXT:
212       case ST_DEFINETEXT2:
213         nid = swf_FontExtract_DefineText(id,f,t,FEDTJ_MODIFY);
214         break;
215     }
216     if (nid>0) id = nid;
217     t = swf_NextTag(t);
218   }
219   return 0;
220 }
221
222 int swf_FontIsItalic(SWFFONT * f) { return f->flags&FF_ITALIC; }
223 int swf_FontIsBold(SWFFONT * f)   { return f->flags&FF_BOLD; }
224
225 int swf_FontSetID(SWFFONT * f,U16 id) { if (!f) return -1; f->id = id; return 0; }
226
227 int swf_FontReduce(SWFFONT * f,FONTUSAGE * use)
228 { int i,j;
229   if ((!f)||(!use)) return -1;
230
231   memset(&f->codes,0x00,sizeof(f->codes));
232
233   j = 0;
234   for (i=0;i<MAX_CHAR_PER_FONT;i++)
235     if (f->glyph[i].shape)
236     { if (use->code[i])
237       { f->glyph[i].gid = j;
238         f->codes[j] = i;
239         j++;
240       }
241       else
242       { swf_ShapeFree(f->glyph[i].shape);
243         f->glyph[i].shape   = 0;
244         f->glyph[i].gid     = 0;
245         f->glyph[i].advance = 0;
246       }
247     } else f->glyph[i].gid = 0;
248     
249   return j;
250 }
251
252 int swf_FontInitUsage(FONTUSAGE * use)
253 { if (!use) return -1;
254   memset(&use->code,0x00,sizeof(use->code));
255   return 0;
256 }
257
258 int swf_FontUse(FONTUSAGE * use,U8 * s)
259 { if ((!use)||(!s)) return -1;
260   while (s[0])
261   { use->code[s[0]] = 1;
262     s++;
263   }
264   return 0;  
265 }
266
267 int swf_FontSetDefine(TAG * t,SWFFONT * f)
268 { U16 ofs[MAX_CHAR_PER_FONT];
269   int p,i,j;
270     
271   if ((!t)||(!f)) return -1;
272   swf_ResetWriteBits(t);
273   swf_SetU16(t,f->id);
274
275   p = 0; j = 0;
276   for (i=0;i<MAX_CHAR_PER_FONT;i++)
277     if (f->glyph[i].shape)
278     { ofs[j++] = p;
279       p+=swf_SetSimpleShape(NULL,f->glyph[i].shape);
280     }
281
282   for (i=0;i<j;i++) swf_SetU16(t,ofs[i]+j*2);
283   
284   for (i=0;i<MAX_CHAR_PER_FONT;i++)
285     if (f->glyph[i].shape)
286       swf_SetSimpleShape(t,f->glyph[i].shape);
287   
288   swf_ResetWriteBits(t);
289   return 0;
290 }
291
292 int swf_FontSetInfo(TAG * t,SWFFONT * f)
293 { int l,i;
294   if ((!t)||(!f)) return -1;
295   swf_ResetWriteBits(t);
296   swf_SetU16(t,f->id);
297   l = strlen(f->name); if (l>255) l = 255;
298   swf_SetU8(t,l);
299   swf_SetBlock(t,f->name,l);
300   swf_SetU8(t,f->flags&0xfe); // no Wide-Codes
301
302   for (i=0;i<MAX_CHAR_PER_FONT;i++)
303     if (f->glyph[i].shape)
304       swf_SetU8(t,i);
305   
306   return 0;
307 }
308
309 int swf_FontExport(int handle,SWFFONT * f)
310 { int l;
311   int i;
312   if (!f) return 0;
313
314   l = sizeof(SWFFONT);
315   if (handle>=0)
316     if (write(handle,f,sizeof(SWFFONT))!=sizeof(SWFFONT)) return -1;
317
318   if (f->name)
319   { U16 ln = strlen(f->name);
320     l+=2+ln;
321     if (handle>=0)
322     { if (write(handle,&ln,2)!=2) return -1;
323       if (write(handle,f->name,ln)!=ln) return -1;
324     }
325   }
326
327   if (f->layout)
328   { l+=sizeof(SWFLAYOUT);
329     if (handle>=0)
330       if (write(handle,f->layout,sizeof(SWFLAYOUT))!=sizeof(SWFLAYOUT)) return -1;
331     if (f->layout->kerning.data)
332     { l+=f->layout->kerning.count*4;
333       if (handle>=0)
334         if (write(handle,f->layout->kerning.data,f->layout->kerning.count*4)!=f->layout->kerning.count*4) return -1;
335     }
336   }
337
338   for (i=0;i<MAX_CHAR_PER_FONT;i++)
339   { if (f->glyph[i].shape)
340     { int ll = swf_ShapeExport(handle,f->glyph[i].shape);
341       if (ll<0) return -1;
342       l+=ll;
343     }  
344   }
345
346   return l;
347 }
348
349 int swf_FontImport(int handle,SWFFONT * * font)
350 { SWFFONT * f;
351   int layout;
352   int i = 0;
353
354   if ((!font)||(handle<0)) return -1;
355
356   f = (SWFFONT *)malloc(sizeof(SWFFONT)); font[0] = f;
357   if (!f) return -1;
358
359   memset(f,0x00,sizeof(SWFFONT));
360   
361   if (read(handle,f,sizeof(SWFFONT))!=sizeof(SWFFONT)) goto fehler;
362
363   layout = (f->layout)?1:0;             // avoid illegal free()
364   f->layout = NULL;
365
366   if (f->name)
367   { U16 ln;
368     f->name = NULL;
369     if (read(handle,&ln,2)!=2) goto fehler;
370     f->name = (U8*)malloc(ln+1);
371     if (!f->name) goto fehler;
372     if (read(handle,f->name,ln)!=ln) goto fehler;
373     f->name[ln] = 0;
374   }
375
376   if (f->layout)
377   { f->layout = (SWFLAYOUT *)malloc(sizeof(SWFLAYOUT));
378     if (!f->layout) goto fehler;
379     if (read(handle,f->layout,sizeof(SWFLAYOUT))!=sizeof(SWFLAYOUT)) goto fehler;
380     if (f->layout->kerning.data)
381     { int l = f->layout->kerning.count*4;
382       f->layout->kerning.data = (U8*)malloc(l);
383       if (!f->layout->kerning.data) goto fehler;
384       if (read(handle,f->layout->kerning.data,l)!=l) goto fehler;
385     }
386   }
387
388   for (i=0;i<MAX_CHAR_PER_FONT;i++)
389   { if (f->glyph[i].shape)
390     { if (swf_ShapeImport(handle,&f->glyph[i].shape)<0) goto fehler;
391     }
392   }
393
394   f->id = 0;
395   
396   return 0;
397   
398 fehler:
399   if (f) for (;i<MAX_CHAR_PER_FONT;i++) f->glyph[i].shape = NULL;
400   swf_FontFree(f);
401   font[0] = NULL;
402   return -1;
403 }
404
405 int swf_TextPrintDefineText(TAG * t,SWFFONT * f)
406 { int id = swf_GetTagID(t);
407   if ((id==ST_DEFINETEXT)||(id==ST_DEFINETEXT2)) swf_FontExtract_DefineText(f->id,f,t,FEDTJ_PRINT);
408     else return -1;
409   return 0;
410 }
411
412 void swf_LayoutFree(SWFLAYOUT * l)
413 { if (l)
414   { if (l->kerning.data) free(l->kerning.data);
415     l->kerning.data = NULL;
416   }
417   free(l);
418 }
419
420 void swf_FontFree(SWFFONT * f)
421 { if (f)
422   { int i;
423       
424     if (f->name) free(f->name);
425     if (f->layout) swf_LayoutFree(f->layout);
426
427     f->name = NULL;
428     f->layout = NULL;
429
430     for (i=0;i<MAX_CHAR_PER_FONT;i++)
431       if (f->glyph[i].shape)
432       { swf_ShapeFree(f->glyph[i].shape);
433         f->glyph[i].shape = NULL;
434       }
435   }
436   free(f);
437 }
438
439 int swf_TextSetInfoRecord(TAG * t,SWFFONT * font,U16 size,RGBA * color,S16 dx,S16 dy)
440 { U8 flags;
441   if (!t) return -1;
442
443   flags = TF_TEXTCONTROL|(font?TF_HASFONT:0)|(color?TF_HASCOLOR:0)|(dx?TF_HASXOFFSET:0)|(dy?TF_HASYOFFSET:0);
444
445   swf_SetU8(t,flags);
446   if (font) swf_SetU16(t,font->id);
447   if (color)
448   { if (swf_GetTagID(t)==ST_DEFINETEXT2) swf_SetRGBA(t,color);
449     else swf_SetRGB(t,color);
450   }
451   if (dx) swf_SetS16(t,dx);
452   if (dy) swf_SetS16(t,dy);
453   if (font) swf_SetU16(t,size);
454   
455   return 0;
456 }
457
458 int swf_TextCountBits(SWFFONT * font,U8 * s,int scale,U8 * gbits,U8 * abits)
459 { U16 g,a;
460   if ((!s)||(!font)||((!gbits)&&(!abits))) return -1;
461   g = a = 0;
462
463   while(s[0])
464   { g = swf_CountBits(font->glyph[s[0]].gid,g);
465     a = swf_CountBits((((U32)font->glyph[s[0]].advance)*scale)/100,a);
466     s++;
467   }
468
469   if (gbits) gbits[0] = (U8)g;
470   if (abits) abits[0] = (U8)a;
471
472   return 0;
473 }
474
475 int swf_TextSetCharRecord(TAG * t,SWFFONT * font,U8 * s,int scale,U8 gbits,U8 abits)
476 { int l,i;
477     
478   if ((!t)||(!font)||(!s)) return -1;
479
480   l = strlen(s);
481   if (l>0x7f) l = 0x7f;
482   swf_SetU8(t,l);
483
484   for (i=0;i<l;i++)
485   { swf_SetBits(t,font->glyph[s[i]].gid,gbits);
486     swf_SetBits(t,(((U32)font->glyph[s[i]].advance)*scale)/100,abits);
487   }
488
489   swf_ResetWriteBits(t);
490   return 0;
491 }
492
493 U32 swf_TextGetWidth(SWFFONT * font,U8 * s,int scale)
494 { U32 res = 0;
495
496   if (font&&s)
497   { while (s[0])
498     { res += font->glyph[s[0]].advance;
499       s++;
500     }
501     if (scale) res = (res*scale)/100;
502   }
503   return res;
504 }
505
506 void swf_WriteFont(SWFFONT*font, char* filename, int useDefineFont2)
507 { SWF swf;
508   TAG * t;
509   SRECT r;
510   RGBA rgb;
511   int f;
512
513   if(useDefineFont2) {
514       fprintf(stderr, "DefineFont2 is not yet supported!\n");
515       useDefineFont2 = 0;
516   }
517
518   font->id = WRITEFONTID; //"FN"
519
520   memset(&swf,0x00,sizeof(SWF));
521
522   swf.fileVersion    = 4;
523   swf.frameRate      = 0x4000;
524   swf.movieSize.xmax = 20*640;
525   swf.movieSize.ymax = 20*480;
526
527   if(!useDefineFont2)
528   /* if we use DefineFont1 to store the characters,
529      we have to build a textfield to store the
530      advance values. While at it, we can also
531      make the whole .swf viewable */
532   {
533       t = swf_InsertTag(NULL,ST_SETBACKGROUNDCOLOR);
534       swf.firstTag = t;
535             rgb.r = 0xff;
536             rgb.g = 0xff;
537             rgb.b = 0xff;
538             swf_SetRGB(t,&rgb);
539       t = swf_InsertTag(t,ST_DEFINEFONT);
540   }
541   else
542   {
543       t = swf_InsertTag(NULL,ST_DEFINEFONT);
544       swf.firstTag = t;
545   }
546
547         swf_FontSetDefine(t,font);
548   
549   t = swf_InsertTag(t,ST_DEFINEFONTINFO);
550         swf_FontSetInfo(t,font);
551
552   if(!useDefineFont2)
553   {
554         int textscale = 400;
555         int s;
556         int xmax = 0;
557         int ymax = textscale * 20;
558         U8 gbits,abits;
559         char text[257];
560         int x,y;
561         text[256]=0;
562         for(s=0;s<256;s++)
563         {
564             text[s] = s;
565             if(font->glyph[s].advance*textscale/100 > xmax)
566                 xmax = font->glyph[s].advance*textscale/100;
567         }
568         swf.movieSize.xmax = xmax*20;
569         swf.movieSize.ymax = ymax;
570
571         t = swf_InsertTag(t,ST_DEFINETEXT);
572
573             swf_SetU16(t,font->id+1);            // ID
574
575             r.xmin = 0;
576             r.ymin = 0;
577             r.xmax = swf.movieSize.xmax*20;
578             r.ymax = swf.movieSize.ymax;
579             
580             swf_SetRect(t,&r);
581
582             swf_SetMatrix(t,NULL);
583
584             abits = swf_CountBits(xmax*16, 0);
585             gbits = 8;
586             
587             swf_SetU8(t,gbits);
588             swf_SetU8(t,abits);
589
590             rgb.r = 0x00;
591             rgb.g = 0x00;
592             rgb.b = 0x00;
593             for(y=0;y<16;y++)
594             {
595                 int c=0,lastx=-1, firstx=0;
596                 for(x=0;x<16;x++) {
597                     if(font->glyph[y*16+x].shape) {
598                         c++;
599                         if(lastx<0) 
600                             lastx = x*xmax;
601                     }
602                 }
603                 if(c) {
604                   swf_TextSetInfoRecord(t,font,textscale,&rgb,lastx+1,textscale*y);
605                   for(x=0;x<16;x++)
606                   {
607                       if(font->glyph[y*16+x].shape) {
608                         if(lastx != x*xmax) {
609                             swf_TextSetInfoRecord(t,0,0,0,x*xmax+1,0);
610                         }
611                         swf_SetU8(t,1);
612                         swf_SetBits(t, font->glyph[y*16+x].gid, gbits);
613                         swf_SetBits(t, font->glyph[y*16+x].advance, abits);
614                         lastx = x*xmax+font->glyph[y*16+x].advance;
615                         swf_ResetWriteBits(t);
616                       }
617                   }
618                 } 
619             }
620             swf_SetU8(t,0);
621         
622         t = swf_InsertTag(t,ST_PLACEOBJECT2);
623
624             swf_ObjectPlace(t,font->id+1,1,NULL,NULL,NULL);
625      
626         t = swf_InsertTag(t,ST_SHOWFRAME);
627   }
628   
629   t = swf_InsertTag(t,ST_END);
630
631   f = open(filename, O_RDWR|O_CREAT|O_TRUNC,0644);
632   if FAILED(swf_WriteSWF(f,&swf)) fprintf(stderr,"WriteSWF() failed in writeFont().\n");
633   close(f);
634
635   swf_FreeTags(&swf);
636 }
637
638 SWFFONT* swf_ReadFont(char* filename)
639 {
640   int f;
641   SWF swf;
642   if(!filename)
643       return 0;
644   f = open(filename,O_RDONLY);
645   
646   if (f<0 || swf_ReadSWF(f,&swf)<0)
647   { fprintf(stderr,"%s is not a valid SWF font file or contains errors.\n",filename);
648     close(f);
649     return 0;
650   }
651   else
652   { SWFFONT*font;
653     close(f);
654     if(swf_FontExtract(&swf, WRITEFONTID, &font) < 0)
655        return 0;
656     swf_FreeTags(&swf);
657     return font;
658   }
659 }
660