1 /* output-emf.c --- output in Enhanced Metafile format
2 
3    Copyright (C) 2000, 2001 Enrico Persiani
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public License
7    as published by the Free Software Foundation; either version 2.1 of
8    the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18    USA. */
19 
20 /*
21 **  Notes:
22 **
23 **  Since EMF files are binary files, their persistent
24 **  format have to deal with endianess problems
25 */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* Def: HAVE_CONFIG_H */
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include "spline.h"
34 #include "xstd.h"
35 
36 /* EMF record-number definitions */
37 
38 #define ENMT_HEADER                1
39 #define ENMT_EOF                  14
40 #define ENMT_POLYLINETO            6
41 #define ENMT_MOVETO               27
42 #define ENMT_POLYBEZIERTO          5
43 #define ENMT_BEGINPATH            59
44 #define ENMT_ENDPATH              60
45 #define ENMT_FILLPATH             62
46 #define ENMT_CREATEPEN            38
47 #define ENMT_CREATEBRUSHINDIRECT  39
48 #define ENMT_SELECTOBJECT         37
49 #define ENMT_SETWORLDTRANSFORM    35
50 #define ENMT_SETPOLYFILLMODE      19
51 #define ENMT_STROKEPATH           64
52 #define ENMT_LINETO               54
53 #define ENMT_POLYBEZIERTO16       88
54 
55 #define STOCK_NULL_PEN 0x80000008
56 
57 #define FM_ALTERNATE 1
58 
59 #define WDEVPIXEL 1280
60 #define HDEVPIXEL 1024
61 #define WDEVMLMTR 320
62 #define HDEVMLMTR 240
63 
64 #define SCALE (at_real) 1.0
65 
66 #define MAKE_COLREF(r,g,b) (((r) & 0x0FF) | (((g) & 0x0FF) << 8) | (((b) & 0x0FF) << 16))
67 #define MK_PEN(n) ((n) * 2 + 1)
68 #define MK_BRUSH(n) ((n) * 2 + 2)
69 #define X_FLOAT_TO_UI32(num) ((UI32)(num * SCALE))
70 #define X_FLOAT_TO_UI16(num) ((UI16)(num * SCALE))
71 #define Y_FLOAT_TO_UI32(num) ((UI32)(y_offset - num * SCALE))
72 #define Y_FLOAT_TO_UI16(num) ((UI16)(y_offset - num * SCALE))
73 
74 /* maybe these definitions be put into types.h
75    with some ifdefs ... */
76 
77 typedef unsigned long int  UI32;
78 typedef unsigned short int UI16;
79 typedef unsigned char      UI8;
80 
81 /* color list type */
82 
83 typedef struct EMFColorListType
84 {
85   UI32 colref;
86   struct EMFColorListType *next;
87 } EMFColorList;
88 
89 /* Emf stats needed for outputting EHNMETAHEADER*/
90 
91 typedef struct
92 {
93   int ncolors;
94   int nrecords;
95   int filesize;
96 } EMFStats;
97 
98 /* globals */
99 
100 static EMFColorList *color_list = NULL;  /* Color list */
101 static UI32 *color_table = NULL;         /* Color table */
102 static float y_offset;
103 
104 /* color list & table functions */
105 
SearchColor(EMFColorList * head,UI32 colref)106 static int SearchColor(EMFColorList *head, UI32 colref)
107 {
108   while(head != NULL)
109   {
110     if(head->colref == colref)
111       return 1;
112     head = head->next;
113   }
114   return 0;
115 }
116 
AddColor(EMFColorList ** head,UI32 colref)117 static void AddColor(EMFColorList **head, UI32 colref)
118 {
119   EMFColorList *temp;
120 
121   XMALLOC(temp, sizeof(EMFColorList));
122 
123   temp->colref = colref;
124   temp->next = *head;
125   *head = temp;
126 }
127 
ColorListToColorTable(EMFColorList ** head,UI32 ** table,int len)128 static void ColorListToColorTable(EMFColorList **head, UI32 **table, int len)
129 {
130   EMFColorList *temp;
131   int i = 0;
132 
133   XMALLOC(*table, sizeof(UI32) * len);
134 
135   while(*head != NULL)
136   {
137     temp = *head;
138     *head = (*head)->next;
139     (*table)[i] = temp->colref;
140     i++;
141     free(temp);
142   }
143 }
144 
ColorLookUp(UI32 colref,UI32 * table,int len)145 static int ColorLookUp(UI32 colref, UI32 *table, int len)
146 {
147   int i;
148 
149   for(i=0; i<len; i++)
150   {
151     if(colref == table[i])
152       return i;
153   }
154   return 0;
155 }
156 
157 /* endianess independent IO functions */
158 
write32(FILE * fdes,UI32 data)159 static at_bool write32(FILE *fdes, UI32 data)
160 {
161   size_t count = 0;
162   UI8 outch;
163 
164   outch = (UI8) (data & 0x0FF);
165   count += fwrite(&outch, 1, 1, fdes);
166 
167   outch = (UI8) ((data >> 8) & 0x0FF);
168   count += fwrite(&outch, 1, 1, fdes);
169 
170   outch = (UI8) ((data >> 16) & 0x0FF);
171   count += fwrite(&outch, 1, 1, fdes);
172 
173   outch = (UI8) ((data >> 24) & 0x0FF);
174   count += fwrite(&outch, 1, 1, fdes);
175 
176   return (count == sizeof(UI32)) ? true : false;
177 }
178 
write16(FILE * fdes,UI16 data)179 static at_bool write16(FILE *fdes, UI16 data)
180 {
181   size_t count = 0;
182   UI8 outch;
183 
184   outch = (UI8) (data & 0x0FF);
185   count += fwrite(&outch, 1, 1, fdes);
186 
187   outch = (UI8) ((data >> 8) & 0x0FF);
188   count += fwrite(&outch, 1, 1, fdes);
189 
190   return (count == sizeof(UI16)) ? true : false;
191 }
192 
193 /* EMF record-type function definitions */
194 
WriteMoveTo(FILE * fdes,at_real_coord * pt)195 static int WriteMoveTo(FILE* fdes, at_real_coord *pt)
196 {
197   int recsize = sizeof(UI32) * 4;
198 
199   if(fdes != NULL)
200   {
201     write32(fdes, ENMT_MOVETO);
202     write32(fdes, (UI32) recsize);
203     write32(fdes, (UI32) X_FLOAT_TO_UI32(pt->x));
204     write32(fdes, (UI32) Y_FLOAT_TO_UI32(pt->y));
205   }
206   return recsize;
207 }
208 
WriteLineTo(FILE * fdes,spline_type * spl)209 static int WriteLineTo(FILE* fdes, spline_type *spl)
210 {
211   int recsize = sizeof(UI32) * 4;
212 
213   if(fdes != NULL)
214   {
215     write32(fdes, ENMT_LINETO);
216     write32(fdes, (UI32) recsize);
217     write32(fdes, (UI32) X_FLOAT_TO_UI32(END_POINT(*spl).x));
218     write32(fdes, (UI32) Y_FLOAT_TO_UI32(END_POINT(*spl).y));
219   }
220   return recsize;
221 }
222 
223 /* CorelDraw 9 can't handle PolyLineTo nor PolyLineTo16, so we
224    do not use this function but divide it to single lines instead
225 int WritePolyLineTo(FILE* fdes, spline_type *spl, int nlines)
226 {
227   int i;
228   int recsize = sizeof(UI32) * (7 + nlines * 1);
229 
230   if(fdes != NULL)
231   {
232     write32(fdes, ENMT_POLYLINETO);
233     write32(fdes, (UI32) recsize);
234     write32(fdes, (UI32) 0x0);
235     write32(fdes, (UI32) 0x0);
236     write32(fdes, (UI32) 0xFFFFFFFF);
237     write32(fdes, (UI32) 0xFFFFFFFF);
238     write32(fdes, (UI32) nlines);
239 
240     for(i=0; i<nlines; i++)
241     {
242       write16(fdes, (UI16) FLOAT_TO_UI32(END_POINT(spl[i]).x));
243       write16(fdes, (UI16) FLOAT_TO_UI32(END_POINT(spl[i]).y));
244     }
245   }
246   return recsize;
247 } */
248 
MyWritePolyLineTo(FILE * fdes,spline_type * spl,int nlines)249 static int MyWritePolyLineTo(FILE* fdes, spline_type *spl, int nlines)
250 {
251   int i;
252   int recsize = nlines * WriteLineTo(NULL, NULL);
253 
254   if(fdes != NULL)
255   {
256     for(i=0; i<nlines; i++)
257     {
258       WriteLineTo(fdes, &spl[i]);
259     }
260   }
261   return recsize;
262 }
263 
264 /* CorelDraw 9 can't handle PolyBezierTo so we do not use this
265    function but use PolyBezierTo16 instead
266 
267 int WritePolyBezierTo(FILE *fdes, spline_type *spl, int ncurves)
268 {
269   int i;
270   int recsize = sizeof(UI32) * (7 + ncurves * 6);
271 
272   if(fdes != NULL)
273   {
274     write32(fdes, ENMT_POLYBEZIERTO);
275     write32(fdes, (UI32) recsize);
276     write32(fdes, (UI32) 0x0);
277     write32(fdes, (UI32) 0x0);
278     write32(fdes, (UI32) 0xFFFFFFFF);
279     write32(fdes, (UI32) 0xFFFFFFFF);
280     write32(fdes, (UI32) ncurves * 3);
281 
282     for(i=0; i<ncurves; i++)
283     {
284       write16(fdes, (UI32) FLOAT_TO_UI32(CONTROL1(spl[i]).x));
285       write16(fdes, (UI32) FLOAT_TO_UI32(CONTROL1(spl[i]).y));
286       write16(fdes, (UI32) FLOAT_TO_UI32(CONTROL2(spl[i]).x));
287       write16(fdes, (UI32) FLOAT_TO_UI32(CONTROL2(spl[i]).y));
288       write16(fdes, (UI32) FLOAT_TO_UI32(END_POINT(spl[i]).x));
289       write16(fdes, (UI32) FLOAT_TO_UI32(END_POINT(spl[i]).y));
290     }
291   }
292   return recsize;
293 } */
294 
WritePolyBezierTo16(FILE * fdes,spline_type * spl,int ncurves)295 static int WritePolyBezierTo16(FILE *fdes, spline_type *spl, int ncurves)
296 {
297   int i;
298   int recsize = sizeof(UI32) * 7 + sizeof(UI16) * ncurves * 6;
299 
300   if(fdes != NULL)
301   {
302     write32(fdes, ENMT_POLYBEZIERTO16);
303     write32(fdes, (UI32) recsize);
304     write32(fdes, (UI32) 0x0);
305     write32(fdes, (UI32) 0x0);
306     write32(fdes, (UI32) 0xFFFFFFFF);
307     write32(fdes, (UI32) 0xFFFFFFFF);
308     write32(fdes, (UI32) ncurves * 3);
309 
310     for(i=0; i<ncurves; i++)
311     {
312       write16(fdes, (UI16) X_FLOAT_TO_UI16(CONTROL1(spl[i]).x));
313       write16(fdes, (UI16) Y_FLOAT_TO_UI16(CONTROL1(spl[i]).y));
314       write16(fdes, (UI16) X_FLOAT_TO_UI16(CONTROL2(spl[i]).x));
315       write16(fdes, (UI16) Y_FLOAT_TO_UI16(CONTROL2(spl[i]).y));
316       write16(fdes, (UI16) X_FLOAT_TO_UI16(END_POINT(spl[i]).x));
317       write16(fdes, (UI16) Y_FLOAT_TO_UI16(END_POINT(spl[i]).y));
318     }
319   }
320   return recsize;
321 }
322 
323 
WriteSetPolyFillMode(FILE * fdes)324 static int WriteSetPolyFillMode(FILE *fdes)
325 {
326   int recsize = sizeof(UI32) * 3;
327 
328   if(fdes != NULL)
329   {
330     write32(fdes, ENMT_SETPOLYFILLMODE);
331     write32(fdes, (UI32) recsize);
332     write32(fdes, (UI32) FM_ALTERNATE);
333   }
334   return recsize;
335 }
336 
WriteBeginPath(FILE * fdes)337 static int WriteBeginPath(FILE *fdes)
338 {
339   int recsize = sizeof(UI32) * 2;
340 
341   if(fdes != NULL)
342   {
343     write32(fdes, ENMT_BEGINPATH);
344     write32(fdes, (UI32) recsize);
345   }
346   return recsize;
347 }
348 
WriteEndPath(FILE * fdes)349 static int WriteEndPath(FILE *fdes)
350 {
351   int recsize = sizeof(UI32) * 2;
352 
353   if(fdes != NULL)
354   {
355     write32(fdes, ENMT_ENDPATH);
356     write32(fdes, (UI32) recsize);
357   }
358   return recsize;
359 }
360 
WriteFillPath(FILE * fdes)361 static int WriteFillPath(FILE *fdes)
362 {
363   int recsize = sizeof(UI32) * 6;
364 
365   if(fdes != NULL)
366   {
367     write32(fdes, ENMT_FILLPATH);
368     write32(fdes, (UI32) recsize);
369     write32(fdes, (UI32) 0x0);
370     write32(fdes, (UI32) 0x0);
371     write32(fdes, (UI32) 0xFFFFFFFF);
372     write32(fdes, (UI32) 0xFFFFFFFF);
373   }
374   return recsize;
375 }
376 
WriteStrokePath(FILE * fdes)377 static int WriteStrokePath(FILE *fdes)
378 {
379   int recsize = sizeof(UI32) * 6;
380 
381   if(fdes != NULL)
382   {
383     write32(fdes, ENMT_STROKEPATH);
384     write32(fdes, (UI32) recsize);
385     write32(fdes, (UI32) 0x0);
386     write32(fdes, (UI32) 0x0);
387     write32(fdes, (UI32) 0xFFFFFFFF);
388     write32(fdes, (UI32) 0xFFFFFFFF);
389   }
390   return recsize;
391 }
392 
393 #if 0
394 static int WriteSetWorldTransform(FILE *fdes, UI32 height)
395 {
396   int recsize = sizeof(UI32) * 8;
397   float fHeight;
398 
399   if(fdes != NULL)
400   {
401 	float s1 = (float) (1.0/SCALE);
402 	float s2 = (float) (1.0/SCALE);
403 	UI32 t1;
404 	UI32 t2;
405     /* conversion to float */
406     fHeight = (float) height;
407     /* binary copy for serialization */
408     memcpy((void *) &height, (void *) &fHeight, sizeof(UI32));
409     memcpy((void *) &t1, (void *) &s1, sizeof(UI32));
410     memcpy((void *) &t2, (void *) &s2, sizeof(UI32));
411 
412     write32(fdes, ENMT_SETWORLDTRANSFORM);
413     write32(fdes, (UI32) recsize);
414     write32(fdes, (UI32) t1);
415     write32(fdes, (UI32) 0x0);
416     write32(fdes, (UI32) 0x0);
417     write32(fdes, (UI32) t2);
418     write32(fdes, (UI32) 0x0);
419     write32(fdes, (UI32) 0x0);
420   }
421   return recsize;
422 }
423 #endif /* 0 */
424 
WriteCreateSolidPen(FILE * fdes,int hndNum,UI32 colref)425 static int WriteCreateSolidPen(FILE *fdes, int hndNum, UI32 colref)
426 {
427   int recsize = sizeof(UI32) * 7;
428 
429   if(fdes != NULL)
430   {
431     write32(fdes, ENMT_CREATEPEN);
432     write32(fdes, (UI32) recsize);
433     write32(fdes, (UI32) hndNum);
434     write32(fdes, (UI32) 0x0);  /* solid pen style */
435     write32(fdes, (UI32) 0x0);  /* 1 pixel ...  */
436     write32(fdes, (UI32) 0x0);  /* ... pen size */
437     write32(fdes, (UI32) colref);
438   }
439   return recsize;
440 }
441 
WriteCreateSolidBrush(FILE * fdes,int hndNum,UI32 colref)442 static int WriteCreateSolidBrush(FILE *fdes, int hndNum, UI32 colref)
443 {
444   int recsize = sizeof(UI32) * 6;
445 
446   if(fdes != NULL)
447   {
448     write32(fdes, ENMT_CREATEBRUSHINDIRECT);
449     write32(fdes, (UI32) recsize);
450     write32(fdes, (UI32) hndNum);
451     write32(fdes, (UI32) 0x0);  /* solid brush style */
452     write32(fdes, (UI32) colref);
453     write32(fdes, (UI32) 0x0);  /* ignored when solid */
454 
455   }
456   return recsize;
457 }
458 
WriteSelectObject(FILE * fdes,int hndNum)459 static int WriteSelectObject(FILE *fdes, int hndNum)
460 {
461   int recsize = sizeof(UI32) * 3;
462 
463   if(fdes != NULL)
464   {
465     write32(fdes, ENMT_SELECTOBJECT);
466     write32(fdes, (UI32) recsize);
467     write32(fdes, (UI32) hndNum);
468   }
469   return recsize;
470 }
471 
WriteEndOfMetafile(FILE * fdes)472 static int WriteEndOfMetafile(FILE *fdes)
473 {
474   int recsize = sizeof(UI32) * 5;
475 
476   if(fdes != NULL)
477   {
478     write32(fdes, ENMT_EOF);
479     write32(fdes, (UI32) recsize);
480     write32(fdes, (UI32) 0);
481     write32(fdes, (UI32) recsize - sizeof(UI32));
482     write32(fdes, (UI32) recsize);
483 
484   }
485   return recsize;
486 }
487 
WriteHeader(FILE * fdes,at_string name,int width,int height,int fsize,int nrec,int nhand)488 static int WriteHeader(FILE *fdes, at_string name, int width, int height, int fsize, int nrec, int nhand)
489 {
490   int i, recsize;
491   size_t desclen;
492   const char * editor = at_version(true);
493 
494   desclen = (strlen(editor) + strlen(name) + 3);
495   recsize = sizeof(UI32) * 25 + (desclen * 2) + ((desclen * 2) % 4);
496 
497   if(fdes != NULL)
498   {
499     write32(fdes, ENMT_HEADER);
500     write32(fdes, (UI32) recsize);
501     /* pixel bounds */
502     write32(fdes, (UI32) 0);
503     write32(fdes, (UI32) 0);
504     write32(fdes, (UI32) width);
505     write32(fdes, (UI32) height);
506     /* millimeter bounds */
507     write32(fdes, (UI32) 0);
508     write32(fdes, (UI32) 0);
509     write32(fdes, (UI32) width * WDEVMLMTR * 100 / WDEVPIXEL);
510     write32(fdes, (UI32) height * HDEVMLMTR * 100 / HDEVPIXEL);
511     /* signature " EMF" */
512     write32(fdes, (UI32) 0x464D4520);
513     /* current version */
514     write32(fdes, (UI32) 0x00010000);
515     /* file size */
516     write32(fdes, (UI32) fsize);
517     /* number of records */
518     write32(fdes, (UI32) nrec);
519     /* number of handles */
520     write16(fdes, (UI16) nhand);
521     /* reserved */
522     write16(fdes, (UI16) 0);
523     /* size of description */
524     write32(fdes, (UI32) desclen);
525     /* description offset */
526     write32(fdes, (UI32) 100);
527     /* palette entries */
528     write32(fdes, (UI32) 0);
529     /* device width & height in pixel & millimeters */
530     write32(fdes, (UI32) WDEVPIXEL);
531     write32(fdes, (UI32) HDEVPIXEL);
532     write32(fdes, (UI32) WDEVMLMTR);
533     write32(fdes, (UI32) HDEVMLMTR);
534     /* pixel format & opengl (not used) */
535     write32(fdes, (UI32) 0);
536     write32(fdes, (UI32) 0);
537     write32(fdes, (UI32) 0);
538     /* description string in Unicode */
539     for(i=0;editor[i]; i++)
540     {
541       write16(fdes, ((UI16) (editor[i] & 0x07F)) );
542     }
543     write16(fdes, (UI16) 0);
544     for(i=0;name[i]; i++)
545     {
546       write16(fdes, ((UI16) (name[i] & 0x07F)) );
547     }
548     write32(fdes, (UI32) 0);
549     if((desclen * 2) % 4)
550       write16(fdes, (UI16) 0);
551   }
552   return recsize;
553 }
554 
555 /* EMF stats collector */
556 
GetEmfStats(EMFStats * stats,at_string name,spline_list_array_type shape)557 static void GetEmfStats(EMFStats *stats, at_string name, spline_list_array_type shape)
558 {
559   unsigned int i, j;
560   int ncolors = 0;
561   int ncolorchng = 0;
562   int nrecords = 0;
563   int filesize = 0;
564   UI32 last_color = 0xFFFFFFFF, curr_color;
565   spline_list_type curr_list;
566   spline_type curr_spline;
567   int last_degree;
568   int nlines;
569 
570   /* visit each spline-list */
571   for(i=0; i<SPLINE_LIST_ARRAY_LENGTH(shape); i++)
572     {
573       curr_list = SPLINE_LIST_ARRAY_ELT(shape, i);
574       curr_color = MAKE_COLREF(curr_list.color.r,curr_list.color.g,curr_list.color.b);
575       if(i == 0 || curr_color != last_color)
576         {
577           ncolorchng++;
578           if(!SearchColor(color_list, curr_color))
579             {
580               ncolors++;
581               AddColor(&color_list, curr_color);
582             }
583           last_color = curr_color;
584           /* emf stats :: BeginPath + EndPath + FillPath */
585           nrecords += 3;
586           filesize += WriteBeginPath(NULL) + WriteEndPath(NULL) + WriteFillPath(NULL);
587         }
588       /* emf stats :: MoveTo */
589       nrecords ++;
590       filesize += WriteMoveTo(NULL,NULL);
591       /* visit each spline */
592 	  j = 0;
593       last_degree = -1;
594       /* the outer loop iterates through spline
595          groups of the same degree */
596       while(j<SPLINE_LIST_LENGTH(curr_list))
597         {
598           nlines = 0;
599           curr_spline = SPLINE_LIST_ELT(curr_list, j);
600           last_degree = ((int)SPLINE_DEGREE(curr_spline));
601 
602           /* the inner loop iterates through lists
603              of spline having the same degree */
604           while(last_degree == ((int)SPLINE_DEGREE(curr_spline)))
605             {
606               nlines++;
607               j++;
608               if(j>=SPLINE_LIST_LENGTH(curr_list))
609                 break;
610               curr_spline = SPLINE_LIST_ELT(curr_list, j);
611             }
612           switch((polynomial_degree)last_degree)
613             {
614               case LINEARTYPE:
615                 /* emf stats :: PolyLineTo */
616                 nrecords += nlines;
617                 filesize += MyWritePolyLineTo(NULL, NULL, nlines);
618                 break;
619               default:
620                 /* emf stats :: PolyBezierTo */
621                 nrecords++;
622                 filesize += WritePolyBezierTo16(NULL, NULL, nlines);
623                 break;
624             }
625         }
626     }
627 
628   /* emf stats :: CreateSolidPen & CreateSolidBrush*/
629   nrecords += ncolors * 2;
630   filesize += (WriteCreateSolidPen(NULL, 0, 0) + WriteCreateSolidBrush(NULL, 0, 0)) * ncolors;
631 
632   /* emf stats :: SelectObject */
633   nrecords += ncolorchng * 2;
634   filesize += WriteSelectObject(NULL, 0) * ncolorchng * 2;
635   /* emf stats :: header + footer */
636   nrecords += 2;
637   filesize += WriteEndOfMetafile(NULL) + WriteHeader(NULL, name, 0, 0, 0, 0, 0);
638 
639   /* emf stats :: SetPolyFillMode */
640   nrecords++;
641   filesize += WriteSetPolyFillMode(NULL);
642 
643   stats->ncolors  = ncolors;
644   stats->nrecords = nrecords;
645   stats->filesize = filesize;
646 
647   /* convert the color list into a color table */
648   ColorListToColorTable(&color_list, &color_table, ncolors);
649 }
650 
651 
652 /* EMF output */
653 
OutputEmf(FILE * fdes,EMFStats * stats,at_string name,int width,int height,spline_list_array_type shape)654 static void OutputEmf(FILE* fdes, EMFStats *stats, at_string name, int width, int height, spline_list_array_type shape)
655 {
656   unsigned int i, j;
657   int color_index;
658   UI32 last_color = 0xFFFFFFFF, curr_color;
659   spline_list_type curr_list;
660   spline_type curr_spline;
661   int last_degree;
662   int nlines;
663 
664   /* output EMF header */
665   WriteHeader(fdes, name, width, height, stats->filesize, stats->nrecords, (stats->ncolors * 2) +1);
666 
667   y_offset = SCALE * height;
668   /* output pens & brushes */
669   for(i=0; i<(unsigned int) stats->ncolors; i++)
670   {
671     WriteCreateSolidPen(fdes, MK_PEN(i), color_table[i]);
672     WriteCreateSolidBrush(fdes, MK_BRUSH(i), color_table[i]);
673   }
674 
675   /* output fill mode */
676   WriteSetPolyFillMode(fdes);
677 
678   /* visit each spline-list */
679   for(i=0; i<SPLINE_LIST_ARRAY_LENGTH(shape); i++)
680   {
681     curr_list = SPLINE_LIST_ARRAY_ELT(shape, i);
682 
683     /* output pen & brush selection */
684     curr_color = MAKE_COLREF(curr_list.color.r,curr_list.color.g,curr_list.color.b);
685     if(i == 0 || curr_color != last_color)
686     {
687       if (i > 0)
688         {
689           /* output EndPath */
690 	      WriteEndPath(fdes);
691 
692 	      if (shape.centerline)
693 	        /* output StrokePath */
694 	        WriteStrokePath(fdes);
695 	      else
696 	        /* output StrokePath */
697 	        WriteFillPath(fdes);
698         }
699       /* output a BeginPath for current shape */
700       WriteBeginPath(fdes);
701 
702       color_index = ColorLookUp(curr_color, color_table, stats->ncolors);
703       if (shape.centerline)
704         WriteSelectObject(fdes, MK_PEN(color_index));
705       else
706         WriteSelectObject(fdes, STOCK_NULL_PEN);
707       WriteSelectObject(fdes, MK_BRUSH(color_index));
708       last_color = curr_color;
709     }
710     /* output MoveTo first point */
711     curr_spline = SPLINE_LIST_ELT(curr_list, 0);
712     WriteMoveTo(fdes, &(START_POINT(curr_spline)));
713 
714     /* visit each spline */
715     j = 0;
716     /* the outer loop iterates through spline
717        groups of the same degree */
718     while (j<SPLINE_LIST_LENGTH(curr_list))
719     {
720       nlines = 0;
721       curr_spline = SPLINE_LIST_ELT(curr_list, j);
722       last_degree = ((int)SPLINE_DEGREE(curr_spline));
723 
724       /* the inner loop iterates through lists
725          of spline having the same degree */
726       while(last_degree == ((int)SPLINE_DEGREE(curr_spline)))
727       {
728         nlines++;
729         j++;
730         if(j>=SPLINE_LIST_LENGTH(curr_list))
731           break;
732         curr_spline = SPLINE_LIST_ELT(curr_list, j);
733       }
734       switch((polynomial_degree)last_degree)
735       {
736         case LINEARTYPE:
737           /* output PolyLineTo */
738           MyWritePolyLineTo(fdes, &(SPLINE_LIST_ELT(curr_list, j - nlines)), nlines);
739           break;
740         default:
741           /* output PolyBezierTo */
742           WritePolyBezierTo16(fdes, &(SPLINE_LIST_ELT(curr_list, j - nlines)), nlines);
743           break;
744       }
745     }
746   }
747   if (SPLINE_LIST_ARRAY_LENGTH(shape) > 0)
748     {
749       /* output EndPath */
750 	  WriteEndPath(fdes);
751 
752 	  if (shape.centerline)
753 	    /* output StrokePath */
754 	    WriteStrokePath(fdes);
755 	  else
756 	    /* output StrokePath */
757 	    WriteFillPath(fdes);
758     }
759 
760   /* output EndOfMetafile */
761   WriteEndOfMetafile(fdes);
762 
763   /* delete color table */
764   free((void *)color_table);
765 }
766 
767 
output_emf_writer(FILE * file,at_string name,int llx,int lly,int urx,int ury,at_output_opts_type * opts,spline_list_array_type shape)768 int output_emf_writer(FILE* file, at_string name,
769 		      int llx, int lly, int urx, int ury,
770 		      at_output_opts_type * opts,
771 		      spline_list_array_type shape)
772 {
773   EMFStats stats;
774 
775 #ifdef _WINDOWS
776     if(file == stdout)
777 	  {
778         fprintf(stderr, "This driver couldn't write to stdout!\n");
779         return -1;
780       }
781 #endif
782 
783   /* Get EMF stats */
784   GetEmfStats(&stats, name, shape);
785 
786   /* Output EMF */
787   OutputEmf(file, &stats, name, urx, ury, shape);
788 
789   return 0;
790 }
791