2004-12-04 Hans Breuer <hans@breuer.org>
[dia.git] / plug-ins / cgm / cgm.c
blob64bb114d50fa18422fd61ea1d670df246cb41957
1 /* -*- Mode: C; c-basic-offset: 4 -*- */
2 /* Dia -- an diagram creation/manipulation program
3 * Copyright (C) 1998 Alexander Larsson
5 * cgm.c -- CGM plugin for dia
6 * Copyright (C) 1999-2000 James Henstridge.
7 * Copyright (C) 2000 Henk Jan Priester.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <stdio.h>
29 #include <string.h>
30 #include <math.h>
31 #include <glib.h>
32 #include <errno.h>
34 #include "intl.h"
35 #include "message.h"
36 #include "geometry.h"
37 #include "filter.h"
38 #include "plug-ins.h"
39 #include "diarenderer.h"
40 #include "dia_image.h"
42 #include <gdk/gdk.h>
44 #define CGM_TYPE_RENDERER (cgm_renderer_get_type ())
45 #define CGM_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CGM_TYPE_RENDERER, CgmRenderer))
46 #define CGM_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CGM_TYPE_RENDERER, CgmRendererClass))
47 #define CGM_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CGM_TYPE_RENDERER))
48 #define CGM_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CGM_TYPE_RENDERER, CgmRendererClass))
50 GType cgm_renderer_get_type (void) G_GNUC_CONST;
52 typedef struct _CgmRenderer CgmRenderer;
53 typedef struct _CgmRendererClass CgmRendererClass;
55 struct _CgmRendererClass
57 DiaRendererClass parent_class;
60 static const gchar *dia_version_string = "Dia-" VERSION;
61 #define IS_ODD(n) (n & 0x01)
63 /* --- routines to write various quantities to the CGM stream. --- */
64 /* signed integers are stored in two complement this is common on Unix */
65 static void
66 write_uint16(FILE *fp, guint16 n)
68 putc( (n & 0xff00) >> 8, fp);
69 putc( n & 0xff, fp);
72 static void
73 write_int16(FILE *fp, gint16 n)
75 write_uint16(fp, (guint16)n);
78 static void
79 write_uint32(FILE *fp, guint32 n)
81 putc((n >> 24) & 0xff, fp);
82 putc((n >> 16) & 0xff, fp);
83 putc((n >> 8) & 0xff, fp);
84 putc(n & 0xff, fp);
87 static void
88 write_int32(FILE *fp, gint32 n)
90 write_uint32(fp, (guint32) n);
93 static void
94 write_colour(FILE *fp, Color *c)
96 putc((int)(c->red * 255), fp);
97 putc((int)(c->green * 255), fp);
98 putc((int)(c->blue * 255), fp);
101 #define REALSIZE 4
102 /* 32 bit fixed point real number */
103 /* stored as 16 bit signed (SI) number and a 16 bit unsigned (UI) for */
104 /* the fraction */
105 /* value is SI + UI / 2^16 */
106 static void
107 write_real(FILE *fp, double x)
109 guint32 n;
111 if ( x < 0 )
113 gint32 wholepart;
114 guint16 fraction;
116 wholepart = (gint32)x;
118 fraction = (guint16)((x - wholepart) * -65536);
119 if ( fraction > 0 )
121 wholepart--;
122 fraction = (guint16)(65536 - fraction);
124 n = (guint32)(wholepart << 16) | fraction;
126 else
127 n = (guint32) (x * (1 << 16));
128 write_uint32(fp, n);
131 static void
132 write_elhead(FILE *fp, int el_class, int el_id, int nparams)
134 guint16 head;
136 head = (el_class & 0x0f) << 12;
137 head += (el_id & 0x7f) << 5;
139 if (nparams >= 31) {
140 /* use long header format */
141 head += 31;
142 write_uint16(fp, head);
143 write_int16(fp, (gint16)nparams);
144 } else {
145 /* use short header format */
146 head += nparams & 0x1f;
147 write_uint16(fp, head);
152 /* --- font stuff --- */
154 static gchar *fontlist;
155 static gint fontlistlen;
156 static GHashTable *fonthash;
157 #define FONT_NUM(font) GPOINTER_TO_INT(g_hash_table_lookup(fonthash, \
158 dia_font_get_family(font)))
160 static void
161 init_fonts(void)
163 /* PANGO FIXME: this is probably broken to some extent now */
164 static gboolean alreadyrun = FALSE;
165 GString *str;
166 gint i;
167 PangoContext *context;
168 PangoFontFamily **families;
169 int n_families;
170 const char *familyname;
172 if (alreadyrun) return;
173 alreadyrun = TRUE;
175 context = gdk_pango_context_get();
176 pango_context_list_families(context,&families,&n_families);
178 fonthash = g_hash_table_new(g_str_hash, g_str_equal);
179 str = g_string_new(NULL);
180 for (i = 0; i < n_families; ++i) {
181 familyname = pango_font_family_get_name(families[i]);
183 g_string_append_c(str, strlen(familyname));
184 g_string_append(str, familyname);
185 g_hash_table_insert(fonthash, (gpointer)familyname,
186 GINT_TO_POINTER(i+1));
188 fontlist = str->str;
189 fontlistlen = str->len;
190 g_string_free(str, FALSE);
193 /* --- CGM line attributes --- */
194 typedef struct _LineAttrCGM
196 int cap;
197 int join;
198 int style;
199 real width;
200 Color color;
202 } LineAttrCGM;
204 /* --- CGM File/Edge attributes --- */
205 typedef struct _FillEdgeAttrCGM
208 int fill_style; /* Fill style */
209 Color fill_color; /* Fill color */
211 int edgevis; /* Edge visibility */
212 int cap; /* Edge cap */
213 int join; /* Edge join */
214 int style; /* Edge style */
215 real width; /* Edge width */
216 Color color; /* Edge color */
218 } FillEdgeAttrCGM;
221 /* --- CGM Text attributes --- */
222 typedef struct _TextAttrCGM
224 int font_num;
225 real font_height;
226 Color color;
228 } TextAttrCGM;
231 /* --- the renderer --- */
233 struct _CgmRenderer
235 DiaRenderer parent_instance;
237 FILE *file;
239 DiaFont *font;
241 real y0, y1;
243 LineAttrCGM lcurrent, linfile;
245 FillEdgeAttrCGM fcurrent, finfile;
247 TextAttrCGM tcurrent, tinfile;
253 static void begin_render(DiaRenderer *self);
254 static void end_render(DiaRenderer *self);
255 static void set_linewidth(DiaRenderer *self, real linewidth);
256 static void set_linecaps(DiaRenderer *self, LineCaps mode);
257 static void set_linejoin(DiaRenderer *self, LineJoin mode);
258 static void set_linestyle(DiaRenderer *self, LineStyle mode);
259 static void set_dashlength(DiaRenderer *self, real length);
260 static void set_fillstyle(DiaRenderer *self, FillStyle mode);
261 static void set_font(DiaRenderer *self, DiaFont *font, real height);
262 static void draw_line(DiaRenderer *self,
263 Point *start, Point *end,
264 Color *line_colour);
265 static void draw_polyline(DiaRenderer *self,
266 Point *points, int num_points,
267 Color *line_colour);
268 static void draw_polygon(DiaRenderer *self,
269 Point *points, int num_points,
270 Color *line_colour);
271 static void fill_polygon(DiaRenderer *self,
272 Point *points, int num_points,
273 Color *line_colour);
274 static void draw_rect(DiaRenderer *self,
275 Point *ul_corner, Point *lr_corner,
276 Color *colour);
277 static void fill_rect(DiaRenderer *self,
278 Point *ul_corner, Point *lr_corner,
279 Color *colour);
280 static void draw_arc(DiaRenderer *self,
281 Point *center,
282 real width, real height,
283 real angle1, real angle2,
284 Color *colour);
285 static void fill_arc(DiaRenderer *self,
286 Point *center,
287 real width, real height,
288 real angle1, real angle2,
289 Color *colour);
290 static void draw_ellipse(DiaRenderer *self,
291 Point *center,
292 real width, real height,
293 Color *colour);
294 static void fill_ellipse(DiaRenderer *self,
295 Point *center,
296 real width, real height,
297 Color *colour);
298 static void draw_bezier(DiaRenderer *self,
299 BezPoint *points,
300 int numpoints,
301 Color *colour);
302 static void fill_bezier(DiaRenderer *self,
303 BezPoint *points, /* Last point must be same as first point */
304 int numpoints,
305 Color *colour);
306 static void draw_string(DiaRenderer *self,
307 const char *text,
308 Point *pos, Alignment alignment,
309 Color *colour);
310 static void draw_image(DiaRenderer *self,
311 Point *point,
312 real width, real height,
313 DiaImage image);
315 static void
316 init_attributes( CgmRenderer *renderer )
318 /* current values, (defaults) */
319 renderer->lcurrent.cap = 3; /* round */
320 renderer->lcurrent.join = 2; /* mitre */
321 renderer->lcurrent.style = 1;
322 renderer->lcurrent.width = 0.1;
323 renderer->lcurrent.color.red = 0;
324 renderer->lcurrent.color.green = 0;
325 renderer->lcurrent.color.blue = 0;
327 renderer->linfile.cap = -1;
328 renderer->linfile.join = -1;
329 renderer->linfile.style = -1;
330 renderer->linfile.width = -1.0;
331 renderer->linfile.color.red = -1;
332 renderer->linfile.color.green = -1;
333 renderer->linfile.color.blue = -1;
335 /* fill/edge defaults */
336 renderer->fcurrent.fill_style = 1; /* solid */
337 renderer->fcurrent.fill_color.red = 0;
338 renderer->fcurrent.fill_color.green = 0;
339 renderer->fcurrent.fill_color.blue = 0;
341 renderer->fcurrent.edgevis = 0;
342 renderer->fcurrent.cap = 3; /* round */
343 renderer->fcurrent.join = 2; /* mitre */
344 renderer->fcurrent.style = 1;
345 renderer->fcurrent.width = 0.1;
346 renderer->fcurrent.color.red = 0;
347 renderer->fcurrent.color.green = 0;
348 renderer->fcurrent.color.blue = 0;
350 renderer->finfile.fill_style = -1;
351 renderer->finfile.fill_color.red = -1;
352 renderer->finfile.fill_color.green = -1;
353 renderer->finfile.fill_color.blue = -1;
355 renderer->finfile.edgevis = -1;
356 renderer->finfile.cap = -1;
357 renderer->finfile.join = -1;
358 renderer->finfile.style = -1;
359 renderer->finfile.width = -1.;
360 renderer->finfile.color.red = -1.0;
361 renderer->finfile.color.green = -1.0;
362 renderer->finfile.color.blue = -1.0;
364 renderer->tcurrent.font_num = 1;
365 renderer->tcurrent.font_height = 0.1;
366 renderer->tcurrent.color.red = 0.0;
367 renderer->tcurrent.color.green = 0.0;
368 renderer->tcurrent.color.blue = 0.0;
370 renderer->tinfile.font_num = -1;
371 renderer->tinfile.font_height = -1.0;
372 renderer->tinfile.color.red = -1.0;
373 renderer->tinfile.color.green = -1.0;
374 renderer->tinfile.color.blue = -1.0;
380 static real
381 swap_y( CgmRenderer *renderer, real y)
383 return (renderer->y0 + renderer->y1 - y);
387 static void
388 write_line_attributes( CgmRenderer *renderer, Color *color )
390 LineAttrCGM *lnew, *lold;
392 lnew = &renderer->lcurrent;
393 lold = &renderer->linfile;
395 if ( lnew->cap != lold->cap )
397 write_elhead(renderer->file, 5, 37, 4);
398 write_int16(renderer->file, lnew->cap);
399 write_int16(renderer->file, 3); /* cap of dashlines match */
400 /* normal cap */
401 lold->cap = lnew->cap;
403 if ( lnew->join != lold->join )
405 write_elhead(renderer->file, 5, 38, 2);
406 write_int16(renderer->file, lnew->join);
407 lold->join = lnew->join;
409 if ( lnew->style != lold->style )
411 write_elhead(renderer->file, 5, 2, 2);
412 write_int16(renderer->file, lnew->style);
413 lold->style = lnew->style;
415 if ( lnew->width != lold->width )
417 write_elhead(renderer->file, 5, 3, REALSIZE);
418 write_real(renderer->file, lnew->width);
419 lold->width = lnew->width;
421 lnew->color = *color;
422 if ( lnew->color.red != lold->color.red ||
423 lnew->color.green != lold->color.green ||
424 lnew->color.blue != lold->color.blue )
426 write_elhead(renderer->file, 5, 4, 3); /* line colour */
427 write_colour(renderer->file, &lnew->color);
428 putc(0, renderer->file);
429 lold->color = lnew->color;
435 ** Update the fill/edge attributes.
437 ** fill_color != NULL, style solid, interrior-color is fill_color
438 ** == NULL, style empty
440 ** edge_color != NULL, edge on with the color and the other
441 ** attributes
442 ** == NULL, edge off
445 static void
446 write_filledge_attributes( CgmRenderer *renderer, Color *fill_color,
447 Color *edge_color )
449 FillEdgeAttrCGM *fnew, *fold;
451 fnew = &renderer->fcurrent;
452 fold = &renderer->finfile;
454 ** Set the edge attributes
457 if ( edge_color == NULL )
459 fnew->edgevis = 0; /* edge off */
460 if ( fnew->edgevis != fold->edgevis )
462 write_elhead(renderer->file, 5, 30, 2);
463 write_int16(renderer->file, fnew->edgevis);
464 fold->edgevis = fnew->edgevis;
467 else
469 fnew->edgevis = 1; /* edge on */
470 if ( fnew->edgevis != fold->edgevis )
472 write_elhead(renderer->file, 5, 30, 2);
473 write_int16(renderer->file, fnew->edgevis);
474 fold->edgevis = fnew->edgevis;
476 if ( fnew->cap != fold->cap )
478 write_elhead(renderer->file, 5, 44, 4);
479 write_int16(renderer->file, fnew->cap);
480 write_int16(renderer->file, 3); /* cap of dashlines match */
481 /* normal cap */
482 fold->cap = fnew->cap;
484 if ( fnew->join != fold->join )
486 write_elhead(renderer->file, 5, 45, 2);
487 write_int16(renderer->file, fnew->join);
488 fold->join = fnew->join;
490 if ( fnew->style != fold->style )
492 write_elhead(renderer->file, 5, 27, 2);
493 write_int16(renderer->file, fnew->style);
494 fold->style = fnew->style;
496 if ( fnew->width != fold->width )
498 write_elhead(renderer->file, 5, 28, REALSIZE);
499 write_real(renderer->file, fnew->width);
500 fold->width = fnew->width;
502 fnew->color = *edge_color;
503 if ( fnew->color.red != fold->color.red ||
504 fnew->color.green != fold->color.green ||
505 fnew->color.blue != fold->color.blue )
507 write_elhead(renderer->file, 5, 29, 3); /* line colour */
508 write_colour(renderer->file, &fnew->color);
509 putc(0, renderer->file);
510 fold->color = fnew->color;
514 if ( fill_color == NULL )
516 fnew->fill_style = 4; /* empty */
517 if ( fnew->fill_style != fold->fill_style )
519 write_elhead(renderer->file, 5, 22, 2);
520 write_int16(renderer->file, fnew->fill_style);
521 fold->fill_style = fnew->fill_style;
524 else
526 fnew->fill_style = 1; /* solid fill */
527 if ( fnew->fill_style != fold->fill_style )
529 write_elhead(renderer->file, 5, 22, 2);
530 write_int16(renderer->file, fnew->fill_style);
531 fold->fill_style = fnew->fill_style;
533 fnew->fill_color = *fill_color;
534 if ( fnew->fill_color.red != fold->fill_color.red ||
535 fnew->fill_color.green != fold->fill_color.green ||
536 fnew->fill_color.blue != fold->fill_color.blue )
538 write_elhead(renderer->file, 5, 23, 3); /* fill colour */
539 write_colour(renderer->file, &fnew->fill_color);
540 putc(0, renderer->file);
541 fold->fill_color = fnew->fill_color;
548 static void
549 write_text_attributes( CgmRenderer *renderer, Color *text_color)
551 TextAttrCGM *tnew, *told;
554 tnew = &renderer->tcurrent;
555 told = &renderer->tinfile;
557 ** Set the text attributes
559 if ( tnew->font_num != told->font_num )
561 write_elhead(renderer->file, 5, 10, 2);
562 write_int16(renderer->file, tnew->font_num);
563 told->font_num = tnew->font_num;
566 if ( tnew->font_height != told->font_height )
568 real h_basecap;
570 /* in CGM we need the base-cap height, I used the 0.9 to correct */
571 /* it because it was still to high. There might be a better way */
572 /* but for now.... */
574 h_basecap = 0.9 * (tnew->font_height -
575 dia_font_descent("Aq",renderer->font,
576 tnew->font_height));
577 write_elhead(renderer->file, 5, 15, REALSIZE);
578 write_real(renderer->file, h_basecap);
579 told->font_height = tnew->font_height;
582 tnew->color = *text_color;
583 if ( tnew->color.red != told->color.red ||
584 tnew->color.green != told->color.green ||
585 tnew->color.blue != told->color.blue )
587 write_elhead(renderer->file, 5, 14, 3); /* text colour */
588 write_colour(renderer->file, &tnew->color);
589 putc(0, renderer->file);
590 told->color = tnew->color;
595 static void
596 begin_render(DiaRenderer *self)
600 static void
601 end_render(DiaRenderer *self)
603 CgmRenderer *renderer = CGM_RENDERER(self);
605 /* end picture */
606 write_elhead(renderer->file, 0, 5, 0);
607 /* end metafile */
608 write_elhead(renderer->file, 0, 2, 0);
610 fclose(renderer->file);
613 static void
614 set_linewidth(DiaRenderer *self, real linewidth)
615 { /* 0 == hairline **/
616 CgmRenderer *renderer = CGM_RENDERER(self);
618 /* update current line and edge width */
619 renderer->lcurrent.width = renderer->fcurrent.width = linewidth;
622 static void
623 set_linecaps(DiaRenderer *self, LineCaps mode)
625 CgmRenderer *renderer = CGM_RENDERER(self);
626 int cap;
628 switch(mode)
630 case LINECAPS_BUTT:
631 cap = 2;
632 break;
633 case LINECAPS_ROUND:
634 cap = 3;
635 break;
636 case LINECAPS_PROJECTING:
637 cap = 4;
638 break;
639 default:
640 cap = 2;
641 break;
643 renderer->lcurrent.cap = renderer->fcurrent.cap = cap;
646 static void
647 set_linejoin(DiaRenderer *self, LineJoin mode)
649 CgmRenderer *renderer = CGM_RENDERER(self);
650 int join;
652 switch(mode)
654 case LINEJOIN_MITER:
655 join = 2;
656 break;
657 case LINEJOIN_ROUND:
658 join = 3;
659 break;
660 case LINEJOIN_BEVEL:
661 join = 4;
662 break;
663 default:
664 join = 2;
665 break;
667 renderer->lcurrent.join = renderer->fcurrent.join = join;
670 static void
671 set_linestyle(DiaRenderer *self, LineStyle mode)
673 CgmRenderer *renderer = CGM_RENDERER(self);
674 gint16 style;
676 switch(mode)
678 case LINESTYLE_DASHED:
679 style = 2;
680 break;
681 case LINESTYLE_DASH_DOT:
682 style = 4;
683 break;
684 case LINESTYLE_DASH_DOT_DOT:
685 style = 5;
686 break;
687 case LINESTYLE_DOTTED:
688 style = 3;
689 break;
690 case LINESTYLE_SOLID:
691 default:
692 style = 1;
693 break;
695 renderer->lcurrent.style = renderer->fcurrent.style = style;
698 static void
699 set_dashlength(DiaRenderer *self, real length)
700 { /* dot = 20% of len */
701 /* CGM doesn't support setting a dash length */
704 static void
705 set_fillstyle(DiaRenderer *self, FillStyle mode)
707 #if 0
708 switch(mode) {
709 case FILLSTYLE_SOLID:
710 write_elhead(renderer->file, 5, 22, 2);
711 write_int16(renderer->file, 1);
712 break;
713 default:
714 message_error("svg_renderer: Unsupported fill mode specified!\n");
716 #endif
719 static void
720 set_font(DiaRenderer *self, DiaFont *font, real height)
722 CgmRenderer *renderer = CGM_RENDERER(self);
724 if (renderer->font != NULL)
725 dia_font_unref(renderer->font);
726 renderer->font = dia_font_ref(font);
727 renderer->tcurrent.font_num = FONT_NUM(font);
728 renderer->tcurrent.font_height = height;
731 static void
732 draw_line(DiaRenderer *self,
733 Point *start, Point *end,
734 Color *line_colour)
736 CgmRenderer *renderer = CGM_RENDERER(self);
738 write_line_attributes(renderer, line_colour);
740 write_elhead(renderer->file, 4, 1, 4 * REALSIZE);
741 write_real(renderer->file, start->x);
742 write_real(renderer->file, swap_y(renderer, start->y));
743 write_real(renderer->file, end->x);
744 write_real(renderer->file, swap_y(renderer, end->y));
747 static void
748 draw_polyline(DiaRenderer *self,
749 Point *points, int num_points,
750 Color *line_colour)
752 CgmRenderer *renderer = CGM_RENDERER(self);
753 int i;
755 write_line_attributes(renderer, line_colour);
757 write_elhead(renderer->file, 4, 1, num_points * 2 * REALSIZE);
758 for (i = 0; i < num_points; i++) {
759 write_real(renderer->file, points[i].x);
760 write_real(renderer->file, swap_y(renderer, points[i].y));
764 static void
765 draw_polygon(DiaRenderer *self,
766 Point *points, int num_points,
767 Color *line_colour)
769 CgmRenderer *renderer = CGM_RENDERER(self);
770 int i;
772 write_filledge_attributes(renderer, NULL, line_colour);
774 write_elhead(renderer->file, 4, 7, num_points * 2 * REALSIZE);
775 for (i = 0; i < num_points; i++) {
776 write_real(renderer->file, points[i].x);
777 write_real(renderer->file, swap_y(renderer, points[i].y));
781 static void
782 fill_polygon(DiaRenderer *self,
783 Point *points, int num_points,
784 Color *colour)
786 CgmRenderer *renderer = CGM_RENDERER(self);
787 int i;
789 write_filledge_attributes(renderer, colour, NULL);
791 write_elhead(renderer->file, 4, 7, num_points * 2 * REALSIZE);
792 for (i = 0; i < num_points; i++) {
793 write_real(renderer->file, points[i].x);
794 write_real(renderer->file, swap_y(renderer, points[i].y));
798 static void
799 draw_rect(DiaRenderer *self,
800 Point *ul_corner, Point *lr_corner,
801 Color *colour)
803 CgmRenderer *renderer = CGM_RENDERER(self);
805 write_filledge_attributes(renderer, NULL, colour);
807 write_elhead(renderer->file, 4, 11, 4 * REALSIZE);
808 write_real(renderer->file, ul_corner->x);
809 write_real(renderer->file, swap_y(renderer, ul_corner->y));
810 write_real(renderer->file, lr_corner->x);
811 write_real(renderer->file, swap_y(renderer, lr_corner->y));
814 static void
815 fill_rect(DiaRenderer *self,
816 Point *ul_corner, Point *lr_corner,
817 Color *colour)
819 CgmRenderer *renderer = CGM_RENDERER(self);
821 write_filledge_attributes(renderer, colour, NULL);
823 write_elhead(renderer->file, 4, 11, 4 * REALSIZE);
824 write_real(renderer->file, ul_corner->x);
825 write_real(renderer->file, swap_y(renderer, ul_corner->y));
826 write_real(renderer->file, lr_corner->x);
827 write_real(renderer->file, swap_y(renderer, lr_corner->y));
832 static void
833 write_ellarc(CgmRenderer *renderer,
834 int elemid,
835 Point *center,
836 real width, real height,
837 real angle1, real angle2 )
839 real rx = width / 2, ry = height / 2;
840 int len;
841 real ynew;
844 ** Angle's are in degrees, need to be converted to 2PI.
846 angle1 = (angle1 / 360.0) * 2 * M_PI;
847 angle2 = (angle2 / 360.0) * 2 * M_PI;
849 ynew = swap_y(renderer, center->y);
852 ** Elliptical Arc (18) or Elliptical Arc close (19).
854 len = elemid == 18 ? (10 * REALSIZE) : (10 * REALSIZE + 2);
855 write_elhead(renderer->file, 4, elemid, len);
856 write_real(renderer->file, center->x); /* center */
857 write_real(renderer->file, ynew);
858 write_real(renderer->file, center->x + rx); /* axes 1 */
859 write_real(renderer->file, ynew);
860 write_real(renderer->file, center->x); /* axes 2 */
861 write_real(renderer->file, ynew + ry);
863 write_real(renderer->file, cos(angle1)); /* vector1 */
864 write_real(renderer->file, sin(angle1));
865 write_real(renderer->file, cos(angle2)); /* vector2 */
866 write_real(renderer->file, sin(angle2));
869 ** Elliptical arc close, use PIE closure.
871 if ( elemid == 19 )
872 write_int16(renderer->file, 0);
876 static void
877 draw_arc(DiaRenderer *self,
878 Point *center,
879 real width, real height,
880 real angle1, real angle2,
881 Color *colour)
883 CgmRenderer *renderer = CGM_RENDERER(self);
885 write_line_attributes(renderer, colour);
886 write_ellarc(renderer, 18, center, width, height, angle1, angle2);
889 static void
890 fill_arc(DiaRenderer *self,
891 Point *center,
892 real width, real height,
893 real angle1, real angle2,
894 Color *colour)
896 CgmRenderer *renderer = CGM_RENDERER(self);
898 write_filledge_attributes(renderer, colour, NULL);
899 write_ellarc(renderer, 19, center, width, height, angle1, angle2);
902 static void
903 draw_ellipse(DiaRenderer *self,
904 Point *center,
905 real width, real height,
906 Color *colour)
908 CgmRenderer *renderer = CGM_RENDERER(self);
909 real ynew;
911 write_filledge_attributes(renderer, NULL, colour);
913 ynew = swap_y(renderer, center->y);
914 write_elhead(renderer->file, 4, 17, 6 * REALSIZE);
915 write_real(renderer->file, center->x); /* center */
916 write_real(renderer->file, ynew);
917 write_real(renderer->file, center->x); /* axes 1 */
918 write_real(renderer->file, ynew + height/2);
919 write_real(renderer->file, center->x + width/2); /* axes 2 */
920 write_real(renderer->file, ynew );
923 static void
924 fill_ellipse(DiaRenderer *self,
925 Point *center,
926 real width, real height,
927 Color *colour)
929 CgmRenderer *renderer = CGM_RENDERER(self);
930 real ynew;
932 write_filledge_attributes(renderer, colour, NULL);
934 ynew = swap_y(renderer, center->y);
935 write_elhead(renderer->file, 4, 17, 6 * REALSIZE);
936 write_real(renderer->file, center->x); /* center */
937 write_real(renderer->file, ynew);
938 write_real(renderer->file, center->x); /* axes 1 */
939 write_real(renderer->file, ynew + height/2);
940 write_real(renderer->file, center->x + width/2); /* axes 2 */
941 write_real(renderer->file, ynew);
945 static void
946 write_bezier(CgmRenderer *renderer,
947 BezPoint *points,
948 int numpoints)
950 int i;
951 Point current;
953 if (points[0].type != BEZ_MOVE_TO)
954 g_warning("first BezPoint must be a BEZ_MOVE_TO");
956 current.x = points[0].p1.x;
957 current.y = swap_y(renderer, points[0].p1.y);
959 for (i = 1; i < numpoints; i++)
961 switch (points[i].type)
963 case BEZ_MOVE_TO:
964 g_warning("only first BezPoint can be a BEZ_MOVE_TO");
965 break;
966 case BEZ_LINE_TO:
967 write_elhead(renderer->file, 4, 1, 4 * REALSIZE);
968 write_real(renderer->file, current.x);
969 write_real(renderer->file, current.y);
970 write_real(renderer->file, points[i].p1.x);
971 write_real(renderer->file, swap_y(renderer, points[i].p1.y));
972 current.x = points[i].p1.x;
973 current.y = swap_y(renderer, points[i].p1.y);
974 break;
975 case BEZ_CURVE_TO:
976 write_elhead(renderer->file, 4, 26, 8 * REALSIZE + 2);
977 write_int16(renderer->file, 1);
978 write_real(renderer->file, current.x);
979 write_real(renderer->file, current.y);
980 write_real(renderer->file, points[i].p1.x);
981 write_real(renderer->file, swap_y(renderer, points[i].p1.y));
982 write_real(renderer->file, points[i].p2.x);
983 write_real(renderer->file, swap_y(renderer, points[i].p2.y));
984 write_real(renderer->file, points[i].p3.x);
985 write_real(renderer->file, swap_y(renderer, points[i].p3.y));
986 current.x = points[i].p3.x;
987 current.y = swap_y(renderer, points[i].p3.y);
988 break;
994 static void
995 draw_bezier(DiaRenderer *self,
996 BezPoint *points,
997 int numpoints,
998 Color *colour)
1000 CgmRenderer *renderer = CGM_RENDERER(self);
1002 if ( numpoints < 2 )
1003 return;
1005 write_line_attributes(renderer, colour);
1006 write_bezier(renderer, points, numpoints);
1010 static void
1011 fill_bezier(DiaRenderer *self,
1012 BezPoint *points, /* Last point must be same as first point */
1013 int numpoints,
1014 Color *colour)
1016 CgmRenderer *renderer = CGM_RENDERER(self);
1018 if ( numpoints < 2 )
1019 return;
1021 write_filledge_attributes(renderer, colour, NULL);
1024 ** A filled bezier is created by using it within a figure.
1026 write_elhead(renderer->file, 0, 8, 0); /* begin figure */
1027 write_bezier(renderer, points, numpoints);
1028 write_elhead(renderer->file, 0, 9, 0); /* end figure */
1033 static void
1034 draw_string(DiaRenderer *self,
1035 const char *text,
1036 Point *pos, Alignment alignment,
1037 Color *colour)
1039 CgmRenderer *renderer = CGM_RENDERER(self);
1040 double x = pos->x, y = swap_y(renderer, pos->y);
1041 gint len, chunk;
1042 const gint maxfirstchunk = 255 - 2 * REALSIZE - 2 - 1;
1043 const gint maxappendchunk = 255 - 2 - 1;
1045 /* check for empty strings */
1046 len = strlen(text);
1047 if ( len == 0 )
1048 return;
1050 write_text_attributes(renderer, colour);
1052 switch (alignment) {
1053 case ALIGN_LEFT:
1054 break;
1055 case ALIGN_CENTER:
1056 x -= dia_font_string_width(text, renderer->font,
1057 renderer->tcurrent.font_height)/2;
1058 break;
1059 case ALIGN_RIGHT:
1060 x -= dia_font_string_width(text, renderer->font,
1061 renderer->tcurrent.font_height);
1062 break;
1064 /* work out size of first chunk of text */
1065 chunk = MIN(maxfirstchunk, len);
1066 write_elhead(renderer->file, 4, 4, 2 * REALSIZE + 2 + 1 + chunk);
1067 write_real(renderer->file, x);
1068 write_real(renderer->file, y);
1069 write_int16(renderer->file, (len == chunk)); /* last chunk? */
1070 putc(chunk, renderer->file);
1071 fwrite(text, sizeof(char), chunk, renderer->file);
1072 if (!IS_ODD(chunk))
1073 putc(0, renderer->file);
1075 len -= chunk;
1076 text += chunk;
1077 while (len > 0) {
1078 /* append text */
1079 chunk = MIN(maxappendchunk, len);
1080 write_elhead(renderer->file, 4, 6, 2 + 1 + chunk);
1081 write_int16(renderer->file, (len == chunk));
1082 putc(chunk, renderer->file);
1083 fwrite(text, sizeof(char), chunk, renderer->file);
1084 if (!IS_ODD(chunk))
1085 putc(0, renderer->file);
1087 len -= chunk;
1088 text += chunk;
1093 static void
1094 draw_image(DiaRenderer *self,
1095 Point *point,
1096 real width, real height,
1097 DiaImage image)
1099 CgmRenderer *renderer = CGM_RENDERER(self);
1100 const gint maxlen = 32767 - 6 * REALSIZE - 4 * 2;
1101 double x1 = point->x, y1 = swap_y(renderer, point->y),
1102 x2 = x1+width, y2 = y1-height;
1103 gint rowlen = dia_image_width(image) * 3, lines = dia_image_height(image);
1104 double linesize = (y1 - y2) / lines;
1105 gint chunk, clines = lines;
1106 guint8 *pImg, *ptr;
1108 if (rowlen > maxlen) {
1109 message_error(_("Image row length larger than maximum cell array.\n"
1110 "Image not exported to CGM."));
1111 return;
1114 ptr = pImg = dia_image_rgb_data(image);
1116 while (lines > 0) {
1117 chunk = MIN(rowlen * lines, maxlen);
1118 clines = chunk / rowlen;
1119 chunk = clines * rowlen;
1121 write_elhead(renderer->file, 4, 9, 6*REALSIZE + 8 + chunk);
1122 write_real(renderer->file, x1); /* first corner */
1123 write_real(renderer->file, y1);
1124 write_real(renderer->file, x2); /* second corner */
1125 write_real(renderer->file, y1 - linesize*clines/*y2*/);
1126 write_real(renderer->file, x2); /* third corner */
1127 write_real(renderer->file, y1);
1129 /* write image size */
1130 write_int16(renderer->file, dia_image_width(image));
1131 write_int16(renderer->file, clines);
1133 write_int16(renderer->file, 8); /* colour precision */
1134 write_int16(renderer->file, 1); /* packed encoding */
1136 fwrite(ptr, sizeof(guint8), chunk, renderer->file);
1138 lines -= clines;
1139 ptr += chunk;
1140 y1 -= clines * linesize;
1142 g_free (pImg);
1145 static void
1146 export_cgm(DiagramData *data, const gchar *filename,
1147 const gchar *diafilename, void* user_data)
1149 CgmRenderer *renderer;
1150 FILE *file;
1151 Rectangle *extent;
1152 gint len;
1154 file = fopen(filename, "wb");
1156 if (file == NULL) {
1157 message_error(_("Can't open output file %s: %s\n"), filename, strerror(errno));
1158 return;
1161 renderer = g_object_new(CGM_TYPE_RENDERER, NULL);
1163 renderer->file = file;
1165 /* write BEGMF */
1166 len = strlen(dia_version_string);
1167 write_elhead(file, 0, 1, len + 1);
1168 putc(len, file);
1169 fwrite(dia_version_string, sizeof(char), len, file);
1170 if (!IS_ODD(len))
1171 putc(0, file);
1173 /* write metafile version */
1174 write_elhead(file, 1, 1, 2);
1175 write_uint16(file, 3); /* use version 3 because we may use polybeziers */
1177 #if 0
1178 /* write metafile description -- use original dia filename */
1179 len = strlen(diafilename);
1180 write_elhead(file, 1, 2, len + 1);
1181 putc(len, file);
1182 fwrite(diafilename, sizeof(char), len, file);
1183 if (!IS_ODD(len))
1184 putc(0, file);
1185 #endif
1187 /* set integer precision */
1188 write_elhead(file, 1, 4, 2);
1189 write_int16(file, 16);
1191 /* write element virtual device unit type */
1192 write_elhead(file, 1, 3, 2);
1193 write_int16(file, 1); /* real number */
1195 /* write the colour precision */
1196 write_elhead(file, 1, 7, 2);
1197 write_int16(file, 8); /* 8 bits per chanel */
1199 /* write element list command */
1200 write_elhead(file, 1, 11, 6);
1201 write_int16(file, 1);
1202 write_int16(file, -1);
1203 write_int16(file, 5);
1205 /* write font list */
1206 init_fonts();
1207 write_elhead(file, 1, 13, fontlistlen);
1208 fwrite(fontlist, sizeof(char), fontlistlen, file);
1209 if (IS_ODD(fontlistlen))
1210 putc(0, file);
1212 /* begin picture */
1213 len = strlen(diafilename);
1214 write_elhead(file, 0, 3, len + 1);
1215 putc(len, file);
1216 fwrite(diafilename, sizeof(char), len, file);
1217 if (!IS_ODD(len))
1218 putc(0, file);
1220 /* write the colour mode string */
1221 write_elhead(file, 2, 2, 2);
1222 write_int16(file, 1); /* direct colour mode (as opposed to indexed) */
1224 /* write edge width mode */
1225 write_elhead(file, 2, 5, 2);
1226 write_int16(file, 0); /* relative to virtual device coordinates */
1228 /* write line width mode */
1229 write_elhead(file, 2, 3, 2);
1230 write_int16(file, 0); /* relative to virtual device coordinates */
1232 extent = &data->extents;
1235 ** Change the swap Y-coordinate in the CGM, to get a more
1236 ** 'portable' CGM
1238 /* write extents */
1239 write_elhead(file, 2, 6, 4 * REALSIZE);
1240 write_real(file, extent->left);
1241 write_real(file, extent->top);
1242 write_real(file, extent->right);
1243 write_real(file, extent->bottom);
1244 renderer->y1 = extent->top;
1245 renderer->y0 = extent->bottom;
1247 /* write back colour */
1248 write_elhead(file, 2, 7, 3);
1249 write_colour(file, &data->bg_color);
1250 putc(0, file);
1252 /* begin the picture body */
1253 write_elhead(file, 0, 4, 0);
1255 /* make text be the right way up */
1256 write_elhead(file, 5, 16, 4 * REALSIZE);
1257 write_real(file, 0);
1258 write_real(file, 1);
1259 write_real(file, 1);
1260 write_real(file, 0);
1262 /* set the text alignment to left/base */
1263 write_elhead(file, 5, 18, 4 + 2 * REALSIZE);
1264 write_int16(file, 1); /* left */
1265 write_int16(file, 4); /* base */
1266 write_real(file, 0.0);
1267 write_real(file, 0.0);
1269 init_attributes(renderer);
1271 data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL);
1273 dia_font_unref(renderer->font);
1274 g_object_unref(renderer);
1277 /* GObject stuff */
1278 static void cgm_renderer_class_init (CgmRendererClass *klass);
1280 static gpointer parent_class = NULL;
1282 GType
1283 cgm_renderer_get_type (void)
1285 static GType object_type = 0;
1287 if (!object_type)
1289 static const GTypeInfo object_info =
1291 sizeof (CgmRendererClass),
1292 (GBaseInitFunc) NULL,
1293 (GBaseFinalizeFunc) NULL,
1294 (GClassInitFunc) cgm_renderer_class_init,
1295 NULL, /* class_finalize */
1296 NULL, /* class_data */
1297 sizeof (CgmRenderer),
1298 0, /* n_preallocs */
1299 NULL /* init */
1302 object_type = g_type_register_static (DIA_TYPE_RENDERER,
1303 "CgmRenderer",
1304 &object_info, 0);
1307 return object_type;
1310 static void
1311 cgm_renderer_finalize (GObject *object)
1313 CgmRenderer *cgm_renderer = CGM_RENDERER (object);
1315 G_OBJECT_CLASS (parent_class)->finalize (object);
1318 static void
1319 cgm_renderer_class_init (CgmRendererClass *klass)
1321 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1322 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
1324 parent_class = g_type_class_peek_parent (klass);
1326 object_class->finalize = cgm_renderer_finalize;
1328 renderer_class->begin_render = begin_render;
1329 renderer_class->end_render = end_render;
1331 renderer_class->set_linewidth = set_linewidth;
1332 renderer_class->set_linecaps = set_linecaps;
1333 renderer_class->set_linejoin = set_linejoin;
1334 renderer_class->set_linestyle = set_linestyle;
1335 renderer_class->set_dashlength = set_dashlength;
1336 renderer_class->set_fillstyle = set_fillstyle;
1337 renderer_class->set_font = set_font;
1339 renderer_class->draw_line = draw_line;
1340 renderer_class->draw_polyline = draw_polyline;
1342 renderer_class->draw_polygon = draw_polygon;
1343 renderer_class->fill_polygon = fill_polygon;
1345 renderer_class->draw_rect = draw_rect;
1346 renderer_class->fill_rect = fill_rect;
1348 renderer_class->draw_arc = draw_arc;
1349 renderer_class->fill_arc = fill_arc;
1351 renderer_class->draw_ellipse = draw_ellipse;
1352 renderer_class->fill_ellipse = fill_ellipse;
1354 renderer_class->draw_bezier = draw_bezier;
1355 renderer_class->fill_bezier = fill_bezier;
1357 renderer_class->draw_string = draw_string;
1359 renderer_class->draw_image = draw_image;
1363 static const gchar *extensions[] = { "cgm", NULL };
1364 static DiaExportFilter cgm_export_filter = {
1365 N_("Computer Graphics Metafile"),
1366 extensions,
1367 export_cgm
1371 /* --- dia plug-in interface --- */
1372 static gboolean
1373 _plugin_can_unload (PluginInfo *info)
1375 return TRUE;
1378 static void
1379 _plugin_unload (PluginInfo *info)
1381 filter_unregister_export(&cgm_export_filter);
1384 DIA_PLUGIN_CHECK_INIT
1386 PluginInitResult
1387 dia_plugin_init(PluginInfo *info)
1389 if (!dia_plugin_info_init(info, "CGM",
1390 _("Computer Graphics Metafile export filter"),
1391 _plugin_can_unload,
1392 _plugin_unload))
1393 return DIA_PLUGIN_INIT_ERROR;
1395 filter_register_export(&cgm_export_filter);
1397 return DIA_PLUGIN_INIT_OK;