linewidth is now working for PS_SOLID
[dia.git] / plug-ins / wmf / wmf.cpp
blob03de2ff3a5453f019a2a1c22c4bd9f24129dc2e8
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * wmf.cpp -- Windows Metafile export plugin for dia
5 * Copyright (C) 2000, Hans Breuer, <Hans@Breuer.Org>
6 * based on dummy plug-in.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "config.h"
24 #include <stdio.h>
25 #include <string.h>
26 #include <math.h>
27 #include <glib.h>
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32 #include "intl.h"
33 #include "message.h"
34 #include "geometry.h"
35 #include "diarenderer.h"
36 #include "filter.h"
37 #include "plug-ins.h"
38 #include "dia_image.h"
40 #ifdef __cplusplus
42 #endif
44 #if defined HAVE_WINDOWS_H || defined G_OS_WIN32
45 namespace W32 {
46 // at least Rectangle conflicts ...
47 #include <windows.h>
49 #else
50 #include "wmf_gdi.h"
51 #define SAVE_EMF
52 #endif
54 /* force linking with gdi32 */
55 #pragma comment( lib, "gdi32" )
58 // #define SAVE_EMF
60 typedef struct _PlaceableMetaHeader
62 guint32 Key; /* Magic number (always 9AC6CDD7h) */
63 guint16 Handle; /* Metafile HANDLE number (always 0) */
64 gint16 Left; /* Left coordinate in metafile units */
65 gint16 Top; /* Top coordinate in metafile units */
66 gint16 Right; /* Right coordinate in metafile units */
67 gint16 Bottom; /* Bottom coordinate in metafile units */
68 guint16 Inch; /* Number of metafile units per inch */
69 guint32 Reserved; /* Reserved (always 0) */
70 guint16 Checksum; /* Checksum value for previous 10 WORDs */
71 } PLACEABLEMETAHEADER;
73 /* --- the renderer --- */
74 G_BEGIN_DECLS
76 #define WMF_TYPE_RENDERER (wmf_renderer_get_type ())
77 #define WMF_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WMF_TYPE_RENDERER, WmfRenderer))
78 #define WMF_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WMF_TYPE_RENDERER, WmfRendererClass))
79 #define WMF_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WMF_TYPE_RENDERER))
80 #define WMF_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WMF_TYPE_RENDERER, WmfRendererClass))
82 GType wmf_renderer_get_type (void) G_GNUC_CONST;
84 typedef struct _WmfRenderer WmfRenderer;
85 typedef struct _WmfRendererClass WmfRendererClass;
87 struct _WmfRenderer
89 DiaRenderer parent_instance;
91 W32::HDC hFileDC;
92 gchar* sFileName;
94 W32::HDC hPrintDC;
96 /* if applicable everything is scaled to 0.01 mm */
97 int nLineWidth; /* need to cache these, because ... */
98 int fnPenStyle; /* ... both are needed at the same time */
99 W32::HPEN hPen; /* ugliness by concept, see DonePen() */
101 W32::HFONT hFont;
102 PLACEABLEMETAHEADER pmh;
103 double xoff, yoff;
104 double scale;
106 int nDashLen; /* the scaled dash length */
107 gboolean platform_is_nt; /* advanced line styles supported */
110 struct _WmfRendererClass
112 DiaRendererClass parent_class;
115 G_END_DECLS
118 * helper macros
120 #define W32COLOR(c) \
121 (W32::COLORREF)( 0xff * c->red + \
122 ((unsigned char)(0xff * c->green)) * 256 + \
123 ((unsigned char)(0xff * c->blue)) * 65536)
125 #define SC(a) ((int)((a)*renderer->scale))
126 #define SCX(a) ((int)(((a)+renderer->xoff)*renderer->scale))
127 #define SCY(a) ((int)(((a)+renderer->yoff)*renderer->scale))
130 * helper functions
132 static W32::HPEN
133 UsePen(WmfRenderer* renderer, Color* colour)
135 W32::HPEN hOldPen;
136 if (colour) {
137 W32::COLORREF rgb = W32COLOR(colour);
138 #ifdef G_OS_WIN32
139 if (renderer->platform_is_nt && renderer->hPrintDC) {
140 W32::LOGBRUSH logbrush;
141 W32::DWORD dashes[6];
142 int num_dashes = 0;
143 int dashlen = renderer->nDashLen;
144 int dotlen = renderer->nDashLen / 10;
146 logbrush.lbStyle = BS_SOLID;
147 logbrush.lbColor = rgb;
148 logbrush.lbHatch = 0;
150 switch (renderer->fnPenStyle & PS_STYLE_MASK) {
151 case PS_SOLID :
152 num_dashes = 2;
153 dashes[0] = dashlen;
154 dashes[1] = 0;
155 break;
156 case PS_DASH :
157 num_dashes = 2;
158 dashes[0] = dashes[1] = dashlen;
159 break;
160 case PS_DASHDOT :
161 num_dashes = 4;
162 dashes[1] = dashes[3] = MAX((dashlen - dotlen) / 2, 1);
163 dashes[0] = dashlen;
164 dashes[2] = dotlen;
165 break;
166 case PS_DASHDOTDOT :
167 num_dashes = 6;
168 dashes[0] = dashlen;
169 dashes[1] = dashes[3] = dashes[5] = MAX((dashlen - 2 * dotlen)/3, 1);
170 dashes[2] = dashes[4] = dotlen;
171 break;
172 case PS_DOT :
173 num_dashes = 2;
174 dashes[0] = dashes[1] = dotlen;
175 break;
176 default :
177 g_assert_not_reached ();
180 renderer->hPen = W32::ExtCreatePen (
181 renderer->fnPenStyle & PS_SOLID ? renderer->fnPenStyle :
182 (renderer->fnPenStyle & ~(PS_STYLE_MASK)) | (PS_GEOMETRIC | PS_USERSTYLE),
183 renderer->nLineWidth,
184 &logbrush, num_dashes, dashes);
187 else
188 #endif /* G_OS_WIN32 */
190 renderer->hPen = W32::CreatePen(renderer->fnPenStyle,
191 renderer->nLineWidth,
192 rgb);
194 } else {
195 renderer->hPen = (W32::HPEN)W32::GetStockObject(NULL_PEN);
197 hOldPen = (W32::HPEN)W32::SelectObject(renderer->hFileDC, renderer->hPen);
198 return hOldPen;
201 static void
202 DonePen(WmfRenderer* renderer, W32::HPEN hPen)
204 /* restore the OLD one ... */
205 if (hPen)
206 W32::SelectObject(renderer->hFileDC, hPen);
207 /* ... before deleting the last active */
208 if (renderer->hPen)
210 W32::DeleteObject(renderer->hPen);
211 renderer->hPen = NULL;
215 #define DIAG_NOTE /* my_log */
216 void
217 my_log(WmfRenderer* renderer, char* format, ...)
219 gchar *string;
220 va_list args;
222 g_return_if_fail (format != NULL);
224 va_start (args, format);
225 string = g_strdup_vprintf (format, args);
226 va_end (args);
228 //fprintf(renderer->file, string);
229 g_print(string);
231 g_free(string);
235 * renderer interface implementation
237 static void
238 begin_render(DiaRenderer *self)
240 WmfRenderer *renderer = WMF_RENDERER (self);
242 DIAG_NOTE(renderer, "begin_render\n");
244 /* FIXME: still not sure if the renderer output should be platform dependent */
245 if (renderer->platform_is_nt)
246 renderer->fnPenStyle = PS_GEOMETRIC;
248 /* make unfilled the default */
249 W32::SelectObject(renderer->hFileDC,
250 W32::GetStockObject (HOLLOW_BRUSH) );
253 static void
254 end_render(DiaRenderer *self)
256 WmfRenderer *renderer = WMF_RENDERER (self);
257 W32::HENHMETAFILE hEmf;
258 W32::UINT nSize;
259 W32::BYTE* pData = NULL;
260 FILE* f;
262 DIAG_NOTE(renderer, "end_render\n");
263 hEmf = W32::CloseEnhMetaFile(renderer->hFileDC);
265 #ifndef SAVE_EMF
266 /* Don't do it when printing */
267 if (renderer->sFileName && strlen (renderer->sFileName)) {
269 W32::HDC hdc = W32::GetDC(NULL);
271 f = fopen(renderer->sFileName, "wb");
273 /* write the placeable header */
274 fwrite(&renderer->pmh, 1, 22 /* NOT: sizeof(PLACEABLEMETAHEADER) */, f);
276 /* get size */
277 nSize = W32::GetWinMetaFileBits(hEmf, 0, NULL, MM_ANISOTROPIC, hdc);
278 pData = g_new(W32::BYTE, nSize);
279 /* get data */
280 nSize = W32::GetWinMetaFileBits(hEmf, nSize, pData, MM_ANISOTROPIC, hdc);
282 /* write file */
283 fwrite(pData,1,nSize,f);
284 fclose(f);
286 g_free(pData);
288 W32::DeleteDC(hdc);
289 } else {
290 W32::RECT r = {0, 0, 0, 0};
291 r.right = W32::GetDeviceCaps (renderer->hPrintDC, PHYSICALWIDTH);
292 r.bottom = W32::GetDeviceCaps (renderer->hPrintDC, PHYSICALHEIGHT);
294 W32::PlayEnhMetaFile (renderer->hPrintDC, hEmf, &r);
296 #endif
297 g_free(renderer->sFileName);
299 if (hEmf)
300 W32::DeleteEnhMetaFile(hEmf);
301 if (renderer->hFont)
302 W32::DeleteObject(renderer->hFont);
305 static void
306 set_linewidth(DiaRenderer *self, real linewidth)
308 WmfRenderer *renderer = WMF_RENDERER (self);
310 DIAG_NOTE(renderer, "set_linewidth %f\n", linewidth);
312 renderer->nLineWidth = SC(linewidth);
315 static void
316 set_linecaps(DiaRenderer *self, LineCaps mode)
318 WmfRenderer *renderer = WMF_RENDERER (self);
320 DIAG_NOTE(renderer, "set_linecaps %d\n", mode);
322 // all the advanced line rendering is unsupported on win9x
323 if (!renderer->platform_is_nt)
324 return;
326 renderer->fnPenStyle &= ~(PS_ENDCAP_MASK);
327 switch(mode) {
328 case LINECAPS_BUTT:
329 renderer->fnPenStyle |= PS_ENDCAP_FLAT;
330 break;
331 case LINECAPS_ROUND:
332 renderer->fnPenStyle |= PS_ENDCAP_ROUND;
333 break;
334 case LINECAPS_PROJECTING:
335 renderer->fnPenStyle |= PS_ENDCAP_SQUARE;
336 break;
337 default:
338 message_error("WmfRenderer : Unsupported fill mode specified!\n");
342 static void
343 set_linejoin(DiaRenderer *self, LineJoin mode)
345 WmfRenderer *renderer = WMF_RENDERER (self);
347 DIAG_NOTE(renderer, "set_join %d\n", mode);
349 if (!renderer->platform_is_nt)
350 return;
352 renderer->fnPenStyle &= ~(PS_JOIN_MASK);
353 switch(mode) {
354 case LINEJOIN_MITER:
355 renderer->fnPenStyle |= PS_JOIN_MITER;
356 break;
357 case LINEJOIN_ROUND:
358 renderer->fnPenStyle |= PS_JOIN_ROUND;
359 break;
360 case LINEJOIN_BEVEL:
361 renderer->fnPenStyle |= PS_JOIN_BEVEL;
362 break;
363 default:
364 message_error("WmfRenderer : Unsupported fill mode specified!\n");
368 static void
369 set_linestyle(DiaRenderer *self, LineStyle mode)
371 WmfRenderer *renderer = WMF_RENDERER (self);
373 DIAG_NOTE(renderer, "set_linestyle %d\n", mode);
375 /* line type */
376 renderer->fnPenStyle &= ~(PS_STYLE_MASK);
377 switch (mode) {
378 case LINESTYLE_SOLID:
379 renderer->fnPenStyle |= PS_SOLID;
380 break;
381 case LINESTYLE_DASHED:
382 renderer->fnPenStyle |= PS_DASH;
383 break;
384 case LINESTYLE_DASH_DOT:
385 renderer->fnPenStyle |= PS_DASHDOT;
386 break;
387 case LINESTYLE_DASH_DOT_DOT:
388 renderer->fnPenStyle |= PS_DASHDOTDOT;
389 break;
390 case LINESTYLE_DOTTED:
391 renderer->fnPenStyle |= PS_DOT;
392 break;
393 default:
394 message_error("WmfRenderer : Unsupported fill mode specified!\n");
397 if (renderer->platform_is_nt)
398 return;
400 /* Non-solid linestyles are only displayed if width <= 1.
401 * Better implementation will require custom linestyles
402 * not available on win9x ...
404 switch (mode) {
405 case LINESTYLE_DASHED:
406 case LINESTYLE_DASH_DOT:
407 case LINESTYLE_DASH_DOT_DOT:
408 case LINESTYLE_DOTTED:
409 renderer->nLineWidth = MIN(renderer->nLineWidth, 1);
410 break;
414 static void
415 set_dashlength(DiaRenderer *self, real length)
417 WmfRenderer *renderer = WMF_RENDERER (self);
419 DIAG_NOTE(renderer, "set_dashlength %f\n", length);
421 /* dot = 10% of len */
422 renderer->nDashLen = SC(length);
425 static void
426 set_fillstyle(DiaRenderer *self, FillStyle mode)
428 WmfRenderer *renderer = WMF_RENDERER (self);
430 DIAG_NOTE(renderer, "set_fillstyle %d\n", mode);
432 switch(mode) {
433 case FILLSTYLE_SOLID:
434 break;
435 default:
436 message_error("WmfRenderer : Unsupported fill mode specified!\n");
440 static void
441 set_font(DiaRenderer *self, DiaFont *font, real height)
443 WmfRenderer *renderer = WMF_RENDERER (self);
445 W32::LPCTSTR sFace;
446 W32::DWORD dwItalic = 0;
447 W32::DWORD dwWeight = FW_DONTCARE;
448 DiaFontStyle style;
450 DIAG_NOTE(renderer, "set_font %s %f\n",
451 dia_font_get_family (font), height);
452 if (renderer->hFont)
453 W32::DeleteObject(renderer->hFont);
455 sFace = dia_font_get_family (font);
456 style = dia_font_get_style(font);
457 dwItalic = DIA_FONT_STYLE_GET_SLANT(style) != DIA_FONT_NORMAL;
459 /* although there is a known algorithm avoid it for cleanness */
460 switch (DIA_FONT_STYLE_GET_WEIGHT(style)) {
461 case DIA_FONT_ULTRALIGHT : dwWeight = FW_ULTRALIGHT; break;
462 case DIA_FONT_LIGHT : dwWeight = FW_LIGHT; break;
463 case DIA_FONT_MEDIUM : dwWeight = FW_MEDIUM; break;
464 case DIA_FONT_DEMIBOLD : dwWeight = FW_DEMIBOLD; break;
465 case DIA_FONT_BOLD : dwWeight = FW_BOLD; break;
466 case DIA_FONT_ULTRABOLD : dwWeight = FW_ULTRABOLD; break;
467 case DIA_FONT_HEAVY : dwWeight = FW_HEAVY; break;
468 default : dwWeight = FW_NORMAL; break;
471 renderer->hFont = (W32::HFONT)W32::CreateFont(
472 - SC (height), // logical height of font
473 0, // logical average character width
474 0, // angle of escapement
475 0, // base-line orientation angle
476 dwWeight, // font weight
477 dwItalic, // italic attribute flag
478 0, // underline attribute flag
479 0, // strikeout attribute flag
480 DEFAULT_CHARSET, // character set identifier
481 OUT_TT_PRECIS, // output precision
482 CLIP_DEFAULT_PRECIS, // clipping precision
483 PROOF_QUALITY, // output quality
484 DEFAULT_PITCH, // pitch and family
485 sFace); // pointer to typeface name string
488 static void
489 draw_line(DiaRenderer *self,
490 Point *start, Point *end,
491 Color *line_colour)
493 WmfRenderer *renderer = WMF_RENDERER (self);
495 W32::HPEN hPen;
497 DIAG_NOTE(renderer, "draw_line %f,%f -> %f, %f\n",
498 start->x, start->y, end->x, end->y);
500 hPen = UsePen(renderer, line_colour);
502 W32::MoveToEx(renderer->hFileDC, SCX(start->x), SCY(start->y), NULL);
503 W32::LineTo(renderer->hFileDC, SCX(end->x), SCY(end->y));
505 DonePen(renderer, hPen);
508 static void
509 draw_polyline(DiaRenderer *self,
510 Point *points, int num_points,
511 Color *line_colour)
513 WmfRenderer *renderer = WMF_RENDERER (self);
515 W32::HPEN hPen;
516 W32::POINT* pts;
517 int i;
519 DIAG_NOTE(renderer, "draw_polyline n:%d %f,%f ...\n",
520 num_points, points->x, points->y);
522 if (num_points < 2)
523 return;
524 pts = g_new (W32::POINT, num_points+1);
525 for (i = 0; i < num_points; i++)
527 pts[i].x = SCX(points[i].x);
528 pts[i].y = SCY(points[i].y);
531 hPen = UsePen(renderer, line_colour);
532 W32::Polyline(renderer->hFileDC, pts, num_points);
533 DonePen(renderer, hPen);
535 g_free(pts);
538 static void
539 draw_polygon(DiaRenderer *self,
540 Point *points, int num_points,
541 Color *line_colour)
543 WmfRenderer *renderer = WMF_RENDERER (self);
545 W32::HPEN hPen;
546 W32::POINT* pts;
547 int i;
549 DIAG_NOTE(renderer, "draw_polygon n:%d %f,%f ...\n",
550 num_points, points->x, points->y);
552 if (num_points < 2)
553 return;
554 pts = g_new (W32::POINT, num_points+1);
555 for (i = 0; i < num_points; i++)
557 pts[i].x = SCX(points[i].x);
558 pts[i].y = SCY(points[i].y);
561 hPen = UsePen(renderer, line_colour);
563 W32::Polygon(renderer->hFileDC, pts, num_points);
565 DonePen(renderer, hPen);
567 g_free(pts);
570 static void
571 fill_polygon(DiaRenderer *self,
572 Point *points, int num_points,
573 Color *colour)
575 WmfRenderer *renderer = WMF_RENDERER (self);
577 W32::HBRUSH hBrush, hBrOld;
578 W32::COLORREF rgb = W32COLOR(colour);
580 DIAG_NOTE(renderer, "fill_polygon n:%d %f,%f ...\n",
581 num_points, points->x, points->y);
583 hBrush = W32::CreateSolidBrush(rgb);
584 hBrOld = (W32::HBRUSH)W32::SelectObject(renderer->hFileDC, hBrush);
586 draw_polygon(self, points, num_points, NULL);
588 W32::SelectObject(renderer->hFileDC,
589 W32::GetStockObject(HOLLOW_BRUSH) );
590 W32::DeleteObject(hBrush);
593 static void
594 draw_rect(DiaRenderer *self,
595 Point *ul_corner, Point *lr_corner,
596 Color *colour)
598 WmfRenderer *renderer = WMF_RENDERER (self);
600 W32::HPEN hPen;
602 DIAG_NOTE(renderer, "draw_rect %f,%f -> %f,%f\n",
603 ul_corner->x, ul_corner->y, lr_corner->x, lr_corner->y);
605 hPen = UsePen(renderer, colour);
607 W32::Rectangle(renderer->hFileDC,
608 SCX(ul_corner->x), SCY(ul_corner->y),
609 SCX(lr_corner->x), SCY(lr_corner->y));
611 DonePen(renderer, hPen);
614 static void
615 fill_rect(DiaRenderer *self,
616 Point *ul_corner, Point *lr_corner,
617 Color *colour)
619 WmfRenderer *renderer = WMF_RENDERER (self);
620 W32::HGDIOBJ hBrush, hBrOld;
621 W32::COLORREF rgb = W32COLOR(colour);
623 DIAG_NOTE(renderer, "fill_rect %f,%f -> %f,%f\n",
624 ul_corner->x, ul_corner->y, lr_corner->x, lr_corner->y);
626 hBrush = W32::CreateSolidBrush(rgb);
627 hBrOld = W32::SelectObject(renderer->hFileDC, hBrush);
629 draw_rect(self, ul_corner, lr_corner, NULL);
631 W32::SelectObject(renderer->hFileDC,
632 W32::GetStockObject (HOLLOW_BRUSH) );
633 W32::DeleteObject(hBrush);
636 static void
637 draw_arc(DiaRenderer *self,
638 Point *center,
639 real width, real height,
640 real angle1, real angle2,
641 Color *colour)
643 WmfRenderer *renderer = WMF_RENDERER (self);
644 W32::HPEN hPen;
645 W32::POINT ptStart, ptEnd;
647 DIAG_NOTE(renderer, "draw_arc %fx%f <%f,<%f @%f,%f\n",
648 width, height, angle1, angle2, center->x, center->y);
650 hPen = UsePen(renderer, colour);
652 /* calculate start and end points of arc */
653 ptStart.x = SCX(center->x + (width / 2.0) * cos((M_PI / 180.0) * angle1));
654 ptStart.y = SCY(center->y - (height / 2.0) * sin((M_PI / 180.0) * angle1));
655 ptEnd.x = SCX(center->x + (width / 2.0) * cos((M_PI / 180.0) * angle2));
656 ptEnd.y = SCY(center->y - (height / 2.0) * sin((M_PI / 180.0) * angle2));
658 W32::MoveToEx(renderer->hFileDC, ptStart.x, ptStart.y, NULL);
659 W32::Arc(renderer->hFileDC,
660 SCX(center->x - width / 2), /* bbox corners */
661 SCY(center->y - height / 2),
662 SCX(center->x + width / 2),
663 SCY(center->y + height / 2),
664 ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
666 DonePen(renderer, hPen);
669 static void
670 fill_arc(DiaRenderer *self,
671 Point *center,
672 real width, real height,
673 real angle1, real angle2,
674 Color *colour)
676 WmfRenderer *renderer = WMF_RENDERER (self);
677 W32::HPEN hPen;
678 W32::HGDIOBJ hBrush, hBrOld;
679 W32::POINT ptStart, ptEnd;
680 W32::COLORREF rgb = W32COLOR(colour);
682 DIAG_NOTE(renderer, "fill_arc %fx%f <%f,<%f @%f,%f\n",
683 width, height, angle1, angle2, center->x, center->y);
685 /* calculate start and end points of arc */
686 ptStart.x = SCX(center->x + (width / 2.0) * cos((M_PI / 180.0) * angle1));
687 ptStart.y = SCY(center->y - (height / 2.0) * sin((M_PI / 180.0) * angle1));
688 ptEnd.x = SCX(center->x + (width / 2.0) * cos((M_PI / 180.0) * angle2));
689 ptEnd.y = SCY(center->y - (height / 2.0) * sin((M_PI / 180.0) * angle2));
691 hPen = UsePen(renderer, NULL); /* no border */
692 hBrush = W32::CreateSolidBrush(rgb);
693 hBrOld = W32::SelectObject(renderer->hFileDC, hBrush);
695 W32::Pie(renderer->hFileDC,
696 SCX(center->x - width / 2), /* bbox corners */
697 SCY(center->y - height / 2),
698 SCX(center->x + width / 2),
699 SCY(center->y + height / 2),
700 ptStart.x, ptStart.y, ptEnd.x, ptEnd.y);
702 W32::SelectObject(renderer->hFileDC,
703 W32::GetStockObject (HOLLOW_BRUSH) );
704 W32::DeleteObject(hBrush);
705 DonePen(renderer, hPen);
708 static void
709 draw_ellipse(DiaRenderer *self,
710 Point *center,
711 real width, real height,
712 Color *colour)
714 WmfRenderer *renderer = WMF_RENDERER (self);
715 W32::HPEN hPen;
717 DIAG_NOTE(renderer, "draw_ellipse %fx%f @ %f,%f\n",
718 width, height, center->x, center->y);
720 hPen = UsePen(renderer, colour);
722 W32::Ellipse(renderer->hFileDC,
723 SCX(center->x - width / 2), /* bbox corners */
724 SCY(center->y - height / 2),
725 SCX(center->x + width / 2),
726 SCY(center->y + height / 2));
728 DonePen(renderer, hPen);
731 static void
732 fill_ellipse(DiaRenderer *self,
733 Point *center,
734 real width, real height,
735 Color *colour)
737 WmfRenderer *renderer = WMF_RENDERER (self);
738 W32::HPEN hPen;
739 W32::HGDIOBJ hBrush, hBrOld;
740 W32::COLORREF rgb = W32COLOR(colour);
742 DIAG_NOTE(renderer, "fill_ellipse %fx%f @ %f,%f\n",
743 width, height, center->x, center->y);
745 hBrush = W32::CreateSolidBrush(rgb);
746 hBrOld = W32::SelectObject(renderer->hFileDC, hBrush);
748 draw_ellipse(self, center, width, height, NULL);
750 W32::SelectObject(renderer->hFileDC,
751 W32::GetStockObject (HOLLOW_BRUSH) );
752 W32::DeleteObject(hBrush);
755 static void
756 draw_bezier(DiaRenderer *self,
757 BezPoint *points,
758 int numpoints,
759 Color *colour)
761 WmfRenderer *renderer = WMF_RENDERER (self);
762 W32::HPEN hPen;
763 W32::POINT * pts;
764 int i;
766 DIAG_NOTE(renderer, "draw_bezier n:%d %fx%f ...\n",
767 numpoints, points->p1.x, points->p1.y);
769 pts = g_new(W32::POINT, (numpoints-1) * 3 + 1);
771 pts[0].x = SCX(points[0].p1.x);
772 pts[0].y = SCY(points[0].p1.y);
774 for (i = 1; i < numpoints; i++)
776 switch(points[i].type)
778 case _BezPoint::BEZ_MOVE_TO:
779 g_warning("only first BezPoint can be a BEZ_MOVE_TO");
780 break;
781 case _BezPoint::BEZ_LINE_TO:
782 /* everyhing the same ?*/
783 pts[i*3-2].x = pts[i*3-1].x =
784 pts[i*3 ].x = SCX(points[i].p1.x);
785 pts[i*3-2].y = pts[i*3-1].y =
786 pts[i*3 ].y = SCY(points[i].p1.y);
787 break;
788 case _BezPoint::BEZ_CURVE_TO:
789 /* control points */
790 pts[i*3-2].x = SCX(points[i].p1.x);
791 pts[i*3-2].y = SCY(points[i].p1.y);
792 pts[i*3-1].x = SCX(points[i].p2.x);
793 pts[i*3-1].y = SCY(points[i].p2.y);
794 /* end point */
795 pts[i*3 ].x = SCX(points[i].p3.x);
796 pts[i*3 ].y = SCY(points[i].p3.y);
797 break;
798 default:
799 break;
803 hPen = UsePen(renderer, colour);
805 W32::PolyBezier(renderer->hFileDC,
806 pts, (numpoints-1)*3+1);
808 DonePen(renderer, hPen);
810 g_free(pts);
813 #ifndef SAVE_EMF
814 /* not defined in compatibility layer */
815 static void
816 fill_bezier(DiaRenderer *self,
817 BezPoint *points, /* Last point must be same as first point */
818 int numpoints,
819 Color *colour)
821 WmfRenderer *renderer = WMF_RENDERER (self);
822 W32::HGDIOBJ hBrush, hBrOld;
823 W32::COLORREF rgb = W32COLOR(colour);
825 DIAG_NOTE(renderer, "fill_bezier n:%d %fx%f ...\n",
826 numpoints, points->p1.x, points->p1.y);
828 hBrush = W32::CreateSolidBrush(rgb);
829 hBrOld = W32::SelectObject(renderer->hFileDC, hBrush);
831 W32::BeginPath (renderer->hFileDC);
832 draw_bezier(self, points, numpoints, NULL);
833 W32::EndPath (renderer->hFileDC);
834 W32::FillPath (renderer->hFileDC);
836 W32::SelectObject(renderer->hFileDC,
837 W32::GetStockObject (HOLLOW_BRUSH) );
838 W32::DeleteObject(hBrush);
840 #endif
842 static void
843 draw_string(DiaRenderer *self,
844 const char *text,
845 Point *pos, Alignment alignment,
846 Color *colour)
848 WmfRenderer *renderer = WMF_RENDERER (self);
849 int len;
850 W32::HGDIOBJ hOld;
851 W32::COLORREF rgb = W32COLOR(colour);
853 DIAG_NOTE(renderer, "draw_string %f,%f %s\n",
854 pos->x, pos->y, text);
856 W32::SetTextColor(renderer->hFileDC, rgb);
858 switch (alignment) {
859 case ALIGN_LEFT:
860 W32::SetTextAlign(renderer->hFileDC, TA_LEFT+TA_BASELINE);
861 break;
862 case ALIGN_CENTER:
863 W32::SetTextAlign(renderer->hFileDC, TA_CENTER+TA_BASELINE);
864 break;
865 case ALIGN_RIGHT:
866 W32::SetTextAlign(renderer->hFileDC, TA_RIGHT+TA_BASELINE);
867 break;
869 /* work out size of first chunk of text */
870 len = strlen(text);
872 hOld = W32::SelectObject(renderer->hFileDC, renderer->hFont);
874 # if 0 // one way to go, but see below ...
875 gint wclen = 0;
876 gunichar2* swc = g_utf8_to_utf16 (text, -1, NULL, &wclen, NULL);
877 W32::TextOutW (renderer->hFileDC,
878 SCX(pos->x), SCY(pos->y),
879 swc, wclen);
880 g_free (swc);
881 # else
882 // works with newest cvs and tml's "official" 2000-12-26 release
883 char* scp;
884 /* convert from utf8 to active codepage */
885 static char codepage[10];
886 sprintf (codepage, "CP%d", W32::GetACP ());
888 scp = g_convert (text, strlen (text),
889 codepage, "UTF-8",
890 NULL, NULL, NULL);
891 if (scp)
893 W32::TextOut(renderer->hFileDC,
894 SCX(pos->x), SCY(pos->y),
895 scp, strlen(scp));
896 g_free (scp);
898 else // converson failed, write unconverted
899 W32::TextOut(renderer->hFileDC,
900 SCX(pos->x), SCY(pos->y),
901 text, strlen (text));
902 # endif
905 W32::SelectObject(renderer->hFileDC, hOld);
908 static void
909 draw_image(DiaRenderer *self,
910 Point *point,
911 real width, real height,
912 DiaImage image)
914 WmfRenderer *renderer = WMF_RENDERER (self);
915 #ifdef SAVE_EMF
916 /* not yet supported in compatibility mode */
917 #else
918 W32::HBITMAP hBmp;
919 int iWidth, iHeight;
920 unsigned char* pData = NULL;
921 unsigned char* pImg = NULL;
923 DIAG_NOTE(renderer, "draw_image %fx%f @%f,%f\n",
924 width, height, point->x, point->y);
926 iWidth = dia_image_width(image);
927 iHeight = dia_image_height(image);
928 pImg = dia_image_rgb_data(image);
930 #if 0 /* only working with 24 bit screen resolution */
931 if ((dia_image_width(image)*3) % 4)
933 /* transform data to be DWORD aligned */
934 int x, y;
935 const unsigned char* pIn = NULL;
936 unsigned char* pOut = NULL;
938 pOut = pData = g_new(unsigned char, ((((iWidth*3-1)/4)+1)*4)*iHeight);
940 pIn = pImg;
941 for (y = 0; y < iHeight; y++)
943 for (x = 0; x < iWidth; x++)
945 *pOut++ = *pIn++;
946 *pOut++ = *pIn++;
947 *pOut++ = *pIn++;
949 pOut += (4 - (iWidth*3)%4);
952 hBmp = W32::CreateBitmap ( iWidth, iHeight, 1, 24, pData);
954 else
956 hBmp = W32::CreateBitmap (
957 dia_image_width(image), dia_image_height(image),
958 1, 24, pImg);
960 W32::HDC hMemDC = W32::CreateCompatibleDC (renderer->hFileDC);
961 #else
962 W32::HDC hMemDC = W32::CreateCompatibleDC (renderer->hFileDC);
963 W32::BITMAPINFO bmi;
964 memset (&bmi, 0, sizeof (W32::BITMAPINFO));
965 bmi.bmiHeader.biSize = sizeof (W32::BITMAPINFOHEADER);
966 bmi.bmiHeader.biWidth = iWidth;
967 bmi.bmiHeader.biHeight = -iHeight; // invert it
968 bmi.bmiHeader.biPlanes = 1;
969 bmi.bmiHeader.biBitCount = 24;
970 bmi.bmiHeader.biCompression = BI_RGB;
971 bmi.bmiHeader.biSizeImage = 0;
972 bmi.bmiHeader.biXPelsPerMeter = 0;
973 bmi.bmiHeader.biYPelsPerMeter = 0;
974 bmi.bmiHeader.biClrUsed = 0;
975 bmi.bmiHeader.biClrImportant = 0;
977 hBmp = W32::CreateDIBSection (hMemDC, &bmi, DIB_RGB_COLORS,
978 (void**)&pData, NULL, 0);
979 /* copy data, always line by line */
980 for (int y = 0; y < iHeight; y ++)
982 int line_offset = dia_image_rowstride(image) * y;
983 for (int x = 0; x < iWidth*3; x+=3)
985 // change from RGB to BGR order
986 pData[x ] = pImg[line_offset + x+2];
987 pData[x+1] = pImg[line_offset + x+1];
988 pData[x+2] = pImg[line_offset + x ];
990 pData += (((3*iWidth-1)/4)+1)*4;
992 pData = NULL; // don't free it below
993 #endif
994 W32::HBITMAP hOldBmp = (W32::HBITMAP)W32::SelectObject(hMemDC, hBmp);
995 //Hack to get SRCCOPY out of namespace W32
996 # ifndef DWORD
997 # define DWORD unsigned long
998 # endif
999 W32::StretchBlt(renderer->hFileDC, // destination
1000 SCX(point->x), SCY(point->y), SC(width), SC(height),
1001 hMemDC, // source
1002 0, 0, iWidth, iHeight, SRCCOPY);
1004 W32::SelectObject (hMemDC, hOldBmp);
1005 W32::DeleteDC (hMemDC);
1006 W32::DeleteObject (hBmp);
1007 if (pData)
1008 g_free (pData);
1009 if (pImg)
1010 g_free (pImg);
1011 #endif /* SAVE_EMF */
1014 //XXX: use RoundRect
1016 /* GObject boiler plate */
1017 static void wmf_renderer_class_init (WmfRendererClass *klass);
1019 static gpointer parent_class = NULL;
1021 GType
1022 wmf_renderer_get_type (void)
1024 static GType object_type = 0;
1026 if (!object_type)
1028 static const GTypeInfo object_info =
1030 sizeof (WmfRendererClass),
1031 (GBaseInitFunc) NULL,
1032 (GBaseFinalizeFunc) NULL,
1033 (GClassInitFunc) wmf_renderer_class_init,
1034 NULL, /* class_finalize */
1035 NULL, /* class_data */
1036 sizeof (WmfRenderer),
1037 0, /* n_preallocs */
1038 NULL /* init */
1041 object_type = g_type_register_static (DIA_TYPE_RENDERER,
1042 "WmfRenderer",
1043 &object_info, (GTypeFlags)0);
1046 return object_type;
1049 static void
1050 wmf_renderer_finalize (GObject *object)
1052 WmfRenderer *wmf_renderer = WMF_RENDERER (object);
1054 G_OBJECT_CLASS (parent_class)->finalize (object);
1057 static void
1058 wmf_renderer_class_init (WmfRendererClass *klass)
1060 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1061 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
1063 parent_class = g_type_class_peek_parent (klass);
1065 object_class->finalize = wmf_renderer_finalize;
1067 /* renderer members */
1068 renderer_class->begin_render = begin_render;
1069 renderer_class->end_render = end_render;
1071 renderer_class->set_linewidth = set_linewidth;
1072 renderer_class->set_linecaps = set_linecaps;
1073 renderer_class->set_linejoin = set_linejoin;
1074 renderer_class->set_linestyle = set_linestyle;
1075 renderer_class->set_dashlength = set_dashlength;
1076 renderer_class->set_fillstyle = set_fillstyle;
1078 renderer_class->set_font = set_font;
1080 renderer_class->draw_line = draw_line;
1081 renderer_class->fill_polygon = fill_polygon;
1082 renderer_class->draw_rect = draw_rect;
1083 renderer_class->fill_rect = fill_rect;
1084 renderer_class->draw_arc = draw_arc;
1085 renderer_class->fill_arc = fill_arc;
1086 renderer_class->draw_ellipse = draw_ellipse;
1087 renderer_class->fill_ellipse = fill_ellipse;
1089 renderer_class->draw_string = draw_string;
1090 renderer_class->draw_image = draw_image;
1092 /* medium level functions */
1093 renderer_class->draw_rect = draw_rect;
1094 renderer_class->draw_polyline = draw_polyline;
1095 renderer_class->draw_polygon = draw_polygon;
1097 renderer_class->draw_bezier = draw_bezier;
1098 #ifndef SAVE_EMF
1099 renderer_class->fill_bezier = fill_bezier;
1100 #endif
1103 /* plug-in export api */
1104 static void
1105 export_data(DiagramData *data, const gchar *filename,
1106 const gchar *diafilename, void* user_data)
1108 WmfRenderer *renderer;
1109 W32::HDC file;
1110 Rectangle *extent;
1111 gint len;
1112 double scale;
1114 W32::RECT bbox;
1116 /* Bounding Box in .01-millimeter units ??? */
1117 bbox.top = 0;
1118 bbox.left = 0;
1119 if ( (data->extents.right - data->extents.left)
1120 > (data->extents.bottom - data->extents.top)) {
1121 scale = floor (32000.0 / (data->extents.right - data->extents.left));
1123 else {
1124 scale = floor (32000.0 / (data->extents.bottom - data->extents.top));
1126 bbox.right = (int)((data->extents.right - data->extents.left) * scale);
1127 bbox.bottom = (int)((data->extents.bottom - data->extents.top) * scale);
1129 file = (W32::HDC)W32::CreateEnhMetaFile(
1130 W32::GetDC(NULL), // handle to a reference device context
1131 #ifdef SAVE_EMF
1132 filename, // pointer to a filename string
1133 #else
1134 NULL, // in memory
1135 #endif
1136 &bbox, // pointer to a bounding rectangle
1137 "Dia\0Diagram\0"); // pointer to an optional description string
1139 if (file == NULL) {
1140 message_error(_("Couldn't open: '%s' for writing.\n"), filename);
1141 return;
1144 renderer = (WmfRenderer*)g_object_new(WMF_TYPE_RENDERER, NULL);
1146 renderer->hFileDC = file;
1147 renderer->sFileName = g_strdup(filename);
1148 renderer->hPrintDC = (W32::HDC)user_data;
1150 /* printing is platform dependent */
1151 renderer->platform_is_nt = (W32::GetVersion () < 0x80000000);
1153 extent = &data->extents;
1155 /* calculate offsets */
1156 renderer->xoff = - data->extents.left;
1157 renderer->yoff = - data->extents.top;
1158 renderer->scale = scale / 25.4; /* don't know why this is required ... */
1160 /* initialize placeable header */
1161 /* bounding box in twips 1/1440 of an inch */
1162 renderer->pmh.Key = 0x9AC6CDD7;
1163 renderer->pmh.Handle = 0;
1164 renderer->pmh.Left = 0;
1165 renderer->pmh.Top = 0;
1166 renderer->pmh.Right = (gint16)(SC(data->extents.right - data->extents.left) * 25.4);
1167 renderer->pmh.Bottom = (gint16)(SC(data->extents.bottom - data->extents.top) * 25.4);
1168 renderer->pmh.Inch = 1440 * 10;
1169 renderer->pmh.Reserved = 0;
1171 guint16 *ptr;
1172 renderer->pmh.Checksum = 0;
1173 for (ptr = (guint16 *)&renderer->pmh; ptr < (guint16 *)&(renderer->pmh.Checksum); ptr++)
1174 renderer->pmh.Checksum ^= *ptr;
1176 #ifdef SAVE_EMF
1177 /* write the placeable header */
1178 fwrite(&renderer->pmh, 1, 22 /* NOT: sizeof(PLACEABLEMETAHEADER) */, file->file);
1179 #endif
1181 /* bounding box in device units */
1182 bbox.left = 0;
1183 bbox.top = 0;
1184 bbox.right = (int)(SC(data->extents.right - data->extents.left) * 25.4);
1185 bbox.bottom = (int)(SC(data->extents.bottom - data->extents.top) * 25.4);
1187 /* initialize drawing */
1188 W32::SetBkMode(renderer->hFileDC, TRANSPARENT);
1189 W32::SetMapMode(renderer->hFileDC, MM_TEXT);
1190 W32::IntersectClipRect(renderer->hFileDC,
1191 bbox.left, bbox.top,
1192 bbox.right, bbox.bottom);
1194 renderer->scale *= 0.95; /* just a little smaller ... */
1196 /* write extents */
1197 DIAG_NOTE(renderer, "export_data extents %f,%f -> %f,%f\n",
1198 extent->left, extent->top, extent->right, extent->bottom);
1200 data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL);
1202 g_object_unref(renderer);
1205 static const gchar *extensions[] = { "wmf", NULL };
1206 static DiaExportFilter my_export_filter = {
1207 N_("Windows Meta File"),
1208 extensions,
1209 export_data,
1210 NULL, /* user data */
1211 "wmf::native"
1215 /* --- dia plug-in interface --- */
1217 DIA_PLUGIN_CHECK_INIT
1219 PluginInitResult
1220 dia_plugin_init(PluginInfo *info)
1222 if (!dia_plugin_info_init(info, "WMF",
1223 _("WMF export filter"),
1224 NULL, NULL))
1225 return DIA_PLUGIN_INIT_ERROR;
1227 #ifdef G_OS_WIN32
1229 * On non windoze platforms this plug-in currently is only
1230 * useful at compile/develoment time. The output is broken
1231 * when processed by wmf_gdi.cpp ...
1233 filter_register_export(&my_export_filter);
1234 #endif
1236 return DIA_PLUGIN_INIT_OK;