/* Dia -- an diagram creation/manipulation program * Copyright (C) 1998 Alexander Larsson * * wpg.c -- WordPerfect Graphics export plugin for dia * Copyright (C) 2000, Hans Breuer, * based on dummy.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * ToDo: * - if helper points - like arc centers - are not in Dia's extent box * their clipping produces unpredictable results * - the font setting needs improvement (maybe on Dia's side) * - bezier curves are not correctly converted to WPG's poly curve * (two point beziers are) * * MayBe: * - full featured import (IMO Dia's import support should be improved * before / on the way ...) */ #include #include #include #include #include #include #include #include "intl.h" #include "message.h" #include "geometry.h" #include "dia_image.h" #include "diarenderer.h" #include "filter.h" #include "plug-ins.h" /* format specific */ #include "wpg_defs.h" /* #define DEBUG_WPG */ /* Import is not yet finished but only implemented to check the * export capabilities and to investigate other programs WPG * formats. * The following is not the reason for abling import, * but it should do it for now ... */ #define WPG_WITH_IMPORT defined (_MSC_VER) /* * helper macros */ #define SC(a) ((a) * renderer->Scale) #define SCX(a) (((a) + renderer->XOffset) * renderer->Scale) #define SCY(a) (((a) + renderer->YOffset) * renderer->Scale) /* --- the renderer --- */ G_BEGIN_DECLS #define WPG_TYPE_RENDERER (wpg_renderer_get_type ()) #define WPG_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WPG_TYPE_RENDERER, WpgRenderer)) #define WPG_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WPG_TYPE_RENDERER, WpgRendererClass)) #define WPG_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WPG_TYPE_RENDERER)) #define WPG_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WPG_TYPE_RENDERER, WpgRendererClass)) GType wpg_renderer_get_type (void) G_GNUC_CONST; typedef struct _WpgRenderer WpgRenderer; typedef struct _WpgRendererClass WpgRendererClass; struct _WpgRenderer { DiaRenderer parent_instance; FILE *file; real Scale; /* to WPU == 1/1200 inch */ real XOffset, YOffset; /* in dia units */ real dash_length; WPGStartData Box; WPGFillAttr FillAttr; WPGLineAttr LineAttr; WPGTextStyle TextStyle; WPGColorRGB* pPal; }; struct _WpgRendererClass { DiaRendererClass parent_class; }; G_END_DECLS #ifdef DEBUG_WPG # define DIAG_NOTE(action) action #else # define DIAG_NOTE(action) #endif #if (G_BYTE_ORDER == G_LITTLE_ENDIAN) /* shortcut if testing of indirection isn't needed anymore */ #define fwrite_le(a,b,c,d) fwrite(a,b,c,d) #else static size_t fwrite_le(void* buf, size_t size, size_t count, FILE* f) { size_t n = 0; guint i; g_assert((1 == size) || (2 == size) || (4 == size)); if (4 == size) { gint32 i32; for (i = 0; i < count; i++) { i32 = GINT32_TO_LE(((gint32*)buf)[i]); n += fwrite(&i32, sizeof(gint32), 1, f); } } else if (2 == size) { gint16 i16; for (i = 0; i < count; i++) { i16 = GINT16_TO_LE(((gint16*)buf)[i]); n += fwrite(&i16, sizeof(gint16), 1, f); } } else n = fwrite(buf, size, count, f); return n; } #endif /* * color cube len, WPG is limited to 256 colors. To get simple * color reduction algorithms only 216 = 6^3 of them are used. */ #define CC_LEN 6 /* * "To avoid problems with WPC products the first 16 colors of * the color map should never be changed". * Define WPG_NUM_DEF_COLORS to 0 ignore this statement (It * appears to work more reliable; tested with WinWord 97 and * Designer 4.1 - the latter does not respect the color maps * start color). */ #define WPG_NUM_DEF_COLORS 0 /* 16 */ /* * helper functions */ static guint8 LookupColor(WpgRenderer *renderer, Color* colour) { /* find nearest color in the renderers color map */ /* the following hack does only work with a correctly sorted color map */ unsigned long i = (int)floor(colour->red * (CC_LEN - 1)) + (int)floor(colour->green * (CC_LEN - 1)) * CC_LEN + (int)floor(colour->blue * (CC_LEN - 1)) * CC_LEN * CC_LEN; DIAG_NOTE(g_message("LookupColor %d.", i)); if (i >= CC_LEN * CC_LEN * CC_LEN) return CC_LEN * CC_LEN * CC_LEN - 1 + WPG_NUM_DEF_COLORS; else return i + WPG_NUM_DEF_COLORS; } static void WriteRecHead(WpgRenderer *renderer, WPG_Type Type, guint32 Size) { if (Size < 255) { WPGHead8 rh; rh.Type = (guint8)Type; rh.Size = (guint8)Size; fwrite(&rh, sizeof(guint8), 2, renderer->file); } else if (Size < 32768) { WPGHead16 rh; rh.Type = (guint8)Type; rh.Dummy = 0xFF; rh.Size = (guint16)Size; fwrite(&rh, sizeof(guint8), 2, renderer->file); fwrite_le(&(rh.Size), sizeof(guint16), 1, renderer->file); } else { WPGHead32 rh; rh.Type = (guint8)Type; rh.Dummy = 0xFF; rh.Size = Size; /* To avoid problems with stucture packing this struct * is written in parts ... */ fwrite(&rh, sizeof(guint8), 2, renderer->file); fwrite_le(&rh.Size, sizeof(guint32), 1, renderer->file); } } static void WriteLineAttr(WpgRenderer *renderer, Color* colour) { g_assert(4 == sizeof(WPGLineAttr)); WriteRecHead(renderer, WPG_LINEATTR, sizeof(WPGLineAttr)); renderer->LineAttr.Color = LookupColor(renderer, colour); fwrite(&renderer->LineAttr, sizeof(guint8), 2, renderer->file); fwrite_le(&renderer->LineAttr.Width, sizeof(guint16), 1, renderer->file); } static void WriteFillAttr(WpgRenderer *renderer, Color* colour, gboolean bFill) { g_assert(2 == sizeof(WPGFillAttr)); WriteRecHead(renderer, WPG_FILLATTR, sizeof(WPGFillAttr)); if (bFill) { renderer->FillAttr.Color = LookupColor(renderer, colour); fwrite(&renderer->FillAttr, sizeof(WPGFillAttr), 1, renderer->file); } else { WPGFillAttr fa; fa.Color = LookupColor(renderer, colour); fa.Type = WPG_FA_HOLLOW; fwrite(&fa, sizeof(WPGFillAttr), 1, renderer->file); } } /* * render functions */ static void begin_render(DiaRenderer *self) { WpgRenderer *renderer = WPG_RENDERER (self); #if 0 const WPGFileHead wpgFileHead = { "\377WPC", 16, 1, 22, 1, 0, /* Version */ 0, 0}; #else /* static conversion to little endian */ const char wpgFileHead[16] = {255, 'W', 'P', 'C', 16, 0, 0, 0, 1, 22, 1, 0, 0, 0, 0, 0}; #endif gint16 i; guint8* pPal; Color color = {1, 1, 1}; DIAG_NOTE(g_message("begin_render")); fwrite(&wpgFileHead, 1, 16, renderer->file); /* bounding box */ WriteRecHead(renderer, WPG_START, sizeof(WPGStartData)); #if 0 fwrite(&renderer->Box, sizeof(WPGStartData), 1, renderer->file); #else g_assert(6 == sizeof(WPGStartData)); fwrite(&renderer->Box, sizeof(guint8), 2, renderer->file); fwrite_le(&renderer->Box.Width, sizeof(guint16), 2, renderer->file); #endif /* initialize a well known colormap, see LookupColor */ pPal = g_new(guint8, CC_LEN*CC_LEN*CC_LEN*3); for (i = 0; i < CC_LEN * CC_LEN * CC_LEN; i++) { pPal[3*i ] = ((i % CC_LEN) * 255) / (CC_LEN - 1); /* red */ pPal[3*i+1] = (((i / CC_LEN) % CC_LEN) * 255) / (CC_LEN - 1); /* green */ pPal[3*i+2] = ((i / (CC_LEN * CC_LEN)) * 255) / (CC_LEN - 1); /* blue varies least */ /* g_print("%d\t%d\t%d\n", pPal[3*i ], pPal[3*i+1], pPal[3*i+2]); */ } WriteRecHead(renderer, WPG_COLORMAP, CC_LEN*CC_LEN*CC_LEN*3 + 2*sizeof(guint16)); i = WPG_NUM_DEF_COLORS; /* start color */ fwrite_le(&i, sizeof(gint16), 1, renderer->file); i = CC_LEN*CC_LEN*CC_LEN; fwrite_le(&i, sizeof(gint16), 1, renderer->file); fwrite(pPal, 1, CC_LEN*CC_LEN*CC_LEN*3, renderer->file); /* FIXME: following 3 lines needed to make filling work !? */ renderer->FillAttr.Type = WPG_FA_SOLID; WriteFillAttr(renderer, &color, TRUE); WriteFillAttr(renderer, &color, FALSE); g_free(pPal); } static void end_render(DiaRenderer *self) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message( "end_render")); WriteRecHead(renderer, WPG_END, 0); /* no data following */ fclose(renderer->file); renderer->file = NULL; } static void set_linewidth(DiaRenderer *self, real linewidth) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("set_linewidth %f", linewidth)); renderer->LineAttr.Width = SC(linewidth); } static void set_linecaps(DiaRenderer *self, LineCaps mode) { DIAG_NOTE(g_message("set_linecaps %d", mode)); switch(mode) { case LINECAPS_BUTT: break; case LINECAPS_ROUND: break; case LINECAPS_PROJECTING: break; default: message_error("WpgRenderer : Unsupported fill mode specified!\n"); } } static void set_linejoin(DiaRenderer *self, LineJoin mode) { DIAG_NOTE(g_message("set_join %d", mode)); switch(mode) { case LINEJOIN_MITER: break; case LINEJOIN_ROUND: break; case LINEJOIN_BEVEL: break; default: message_error("WpgRenderer : Unsupported fill mode specified!\n"); } } static void set_linestyle(DiaRenderer *self, LineStyle mode) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("set_linestyle %d", mode)); /* line type */ switch (mode) { case LINESTYLE_SOLID: renderer->LineAttr.Type = WPG_LA_SOLID; break; case LINESTYLE_DASHED: if (renderer->dash_length < 0.5) renderer->LineAttr.Type = WPG_LA_SHORTDASH; else renderer->LineAttr.Type = WPG_LA_MEDIUMDASH; break; case LINESTYLE_DASH_DOT: renderer->LineAttr.Type = WPG_LA_DASHDOT; break; case LINESTYLE_DASH_DOT_DOT: renderer->LineAttr.Type = WPG_LA_DASHDOTDOT; break; case LINESTYLE_DOTTED: renderer->LineAttr.Type = WPG_LA_DOTS; break; default: message_error("WpgRenderer : Unsupported fill mode specified!\n"); } } static void set_dashlength(DiaRenderer *self, real length) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("set_dashlength %f", length)); /* dot = 20% of len */ renderer->dash_length = length; } static void set_fillstyle(DiaRenderer *self, FillStyle mode) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("set_fillstyle %d", mode)); switch(mode) { case FILLSTYLE_SOLID: renderer->FillAttr.Type = WPG_FA_SOLID; break; default: message_error("WpgRenderer : Unsupported fill mode specified!\n"); } } static void set_font(DiaRenderer *self, DiaFont *font, real height) { WpgRenderer *renderer = WPG_RENDERER (self); /* FIXME PANGO: this is a little broken. Use better matching. */ const char *family_name; DIAG_NOTE(g_message("set_font %f %s", height, font->name)); renderer->TextStyle.Height = SC(height); family_name = dia_font_get_family(font); if ((strstr(family_name, "courier")) || (strstr(family_name, "monospace"))) renderer->TextStyle.Font = 0x0DF0; else if ((strstr(family_name, "times")) || (strstr(family_name, "serif"))) renderer->TextStyle.Font = 0x1950; else renderer->TextStyle.Font = 0x1150; /* Helv */ } /* Need to translate coord system: * * Dia x,y -> Wpg x,-y * * doing it before scaling. */ static void draw_line(DiaRenderer *self, Point *start, Point *end, Color *line_colour) { WpgRenderer *renderer = WPG_RENDERER (self); gint16 pData[4]; DIAG_NOTE(g_message("draw_line %f,%f -> %f, %f", start->x, start->y, end->x, end->y)); WriteLineAttr(renderer, line_colour); WriteRecHead(renderer, WPG_LINE, 4 * sizeof(gint16)); /* point data */ pData[0] = SCX(start->x); pData[1] = SCY(-start->y); pData[2] = SCX(end->x); pData[3] = SCY(-end->y); fwrite_le(pData, sizeof(gint16), 4, renderer->file); } static void draw_polyline(DiaRenderer *self, Point *points, int num_points, Color *line_colour) { WpgRenderer *renderer = WPG_RENDERER (self); int i; gint16* pData; DIAG_NOTE(g_message("draw_polyline n:%d %f,%f ...", num_points, points->x, points->y)); g_return_if_fail(1 < num_points); WriteLineAttr(renderer, line_colour); WriteRecHead(renderer, WPG_POLYLINE, num_points * 2 * sizeof(gint16) + sizeof(gint16)); pData = g_new(gint16, num_points * 2); /* number of points */ pData[0] = num_points; fwrite_le(pData, sizeof(gint16), 1, renderer->file); /* point data */ for (i = 0; i < num_points; i++) { pData[2*i] = SCX(points[i].x); pData[2*i+1] = SCY(-points[i].y); } fwrite_le(pData, sizeof(gint16), num_points*2, renderer->file); g_free(pData); } static void draw_polygon(DiaRenderer *self, Point *points, int num_points, Color *line_colour) { WpgRenderer *renderer = WPG_RENDERER (self); gint16* pData; int i; DIAG_NOTE(g_message("draw_polygon n:%d %f,%f ...", num_points, points->x, points->y)); WriteLineAttr(renderer, line_colour); WriteRecHead(renderer, WPG_POLYGON, (num_points * 2 + 1) * sizeof(gint16)); pData = g_new(gint16, num_points * 2); /* number of vertices */ pData[0] = num_points; fwrite_le(pData, sizeof(gint16), 1, renderer->file); /* point data */ for (i = 0; i < num_points; i++) { pData[2*i] = SCX(points[i].x); pData[2*i+1] = SCY(-points[i].y); } fwrite_le(pData, sizeof(gint16), num_points*2, renderer->file); g_free(pData); } static void fill_polygon(DiaRenderer *self, Point *points, int num_points, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("fill_polygon n:%d %f,%f ...", num_points, points->x, points->y)); WriteFillAttr(renderer, colour, TRUE); draw_polygon(self,points,num_points,colour); WriteFillAttr(renderer, colour, FALSE); } static void draw_rect(DiaRenderer *self, Point *ul_corner, Point *lr_corner, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); gint16* pData; DIAG_NOTE(g_message("draw_rect %f,%f -> %f,%f", ul_corner->x, ul_corner->y, lr_corner->x, lr_corner->y)); WriteLineAttr(renderer, colour); WriteRecHead(renderer, WPG_RECTANGLE, 4*sizeof(gint16)); pData = g_new(gint16, 4); pData[0] = SCX(ul_corner->x); /* lower left corner ! */ pData[1] = SCY(-lr_corner->y); pData[2] = SC(lr_corner->x - ul_corner->x); /* width */ pData[3] = SC(lr_corner->y - ul_corner->y); /* height */ fwrite_le(pData, sizeof(gint16), 4, renderer->file); g_free(pData); } static void fill_rect(DiaRenderer *self, Point *ul_corner, Point *lr_corner, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("fill_rect %f,%f -> %f,%f", ul_corner->x, ul_corner->y, lr_corner->x, lr_corner->y)); WriteFillAttr(renderer, colour, TRUE); draw_rect(self, ul_corner, lr_corner, colour); WriteFillAttr(renderer, colour, FALSE); } static void draw_arc(DiaRenderer *self, Point *center, real width, real height, real angle1, real angle2, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); WPGEllipse ell; DIAG_NOTE(g_message("draw_arc %fx%f <%f,<%f", width, height, angle1, angle2)); ell.x = SCX(center->x); ell.y = SCY(-center->y); ell.RotAngle = 0; ell.rx = SC(width / 2.0); ell.ry = SC(height / 2.0); ell.StartAngle = angle1; ell.EndAngle = angle2; ell.Flags = 0; WriteLineAttr(renderer, colour); WriteRecHead(renderer, WPG_ELLIPSE, sizeof(WPGEllipse)); g_assert(16 == sizeof(WPGEllipse)); fwrite_le(&ell, sizeof(guint16), sizeof(WPGEllipse) / sizeof(guint16), renderer->file); } static void fill_arc(DiaRenderer *self, Point *center, real width, real height, real angle1, real angle2, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); WPGEllipse ell; DIAG_NOTE(g_message("fill_arc %fx%f <%f,<%f", width, height, angle1, angle2)); ell.x = SCX(center->x); ell.y = SCY(-center->y); ell.RotAngle = 0; ell.rx = SC(width / 2.0); ell.ry = SC(height / 2.0); ell.StartAngle = angle1; ell.EndAngle = angle2; ell.Flags = 0; /* 0: connect to center; 1: connect start and end */ WriteLineAttr(renderer, colour); WriteFillAttr(renderer, colour, TRUE); WriteRecHead(renderer, WPG_ELLIPSE, sizeof(WPGEllipse)); g_assert(16 == sizeof(WPGEllipse)); fwrite_le(&ell, sizeof(guint16), sizeof(WPGEllipse) / sizeof(guint16), renderer->file); WriteFillAttr(renderer, colour, FALSE); } static void draw_ellipse(DiaRenderer *self, Point *center, real width, real height, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); WPGEllipse ell; DIAG_NOTE(g_message("draw_ellipse %fx%f center @ %f,%f", width, height, center->x, center->y)); ell.x = SCX(center->x); ell.y = SCY(-center->y); ell.RotAngle = 0; ell.rx = SC(width / 2.0); ell.ry = SC(height / 2.0); ell.StartAngle = 0; ell.EndAngle = 360; ell.Flags = 0; WriteLineAttr(renderer, colour); WriteRecHead(renderer, WPG_ELLIPSE, sizeof(WPGEllipse)); g_assert(16 == sizeof(WPGEllipse)); fwrite_le(&ell, sizeof(guint16), sizeof(WPGEllipse) / sizeof(guint16), renderer->file); } static void fill_ellipse(DiaRenderer *self, Point *center, real width, real height, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); DIAG_NOTE(g_message("fill_ellipse %fx%f center @ %f,%f", width, height, center->x, center->y)); WriteFillAttr(renderer, colour, TRUE); draw_ellipse(self,center,width,height,colour); WriteFillAttr(renderer, colour, FALSE); } #ifdef USE_OUR_DRAW_BEZIER static void draw_bezier(DiaRenderer *self, BezPoint *points, int numpoints, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); gint16* pData; int i; DIAG_NOTE(g_message("draw_bezier n:%d %fx%f ...", numpoints, points->p1.x, points->p1.y)); WriteLineAttr(renderer, colour); WriteRecHead(renderer, WPG_POLYCURVE, (numpoints * 2) * 2 * sizeof(gint16) + 3 * sizeof(gint16)); pData = g_new(gint16, numpoints * 6); /* ??? size of equivalent data in pre5.1 files ? */ #if 1 memset(pData, 0, sizeof(gint16)*2); #else /* try first point instead */ pData[0] = SCX(points[0].p1.x); pData[1] = SCY(-points[0].p1.y); #endif fwrite_le(pData, sizeof(gint16), 2, renderer->file); pData[0] = numpoints * 2; fwrite_le(pData, sizeof(gint16), 1, renderer->file); /* WPG's Poly Curve is not quite the same as DIA's Bezier. * There is only one Control Point per Point and I can't * get it right, but at least they are read without error. */ for (i = 0; i < numpoints; i++) { switch (points[i].type) { case BEZ_MOVE_TO: case BEZ_LINE_TO: /* real point */ pData[4*i ] = SCX( points[i].p1.x); pData[4*i+1] = SCY(-points[i].p1.y); /* control point (1st from next point) */ if (i+1 < numpoints) { pData[4*i+2] = SCX( points[i+1].p1.x); pData[4*i+3] = SCY(-points[i+1].p1.y); } else { pData[4*i+2] = SCX( points[i].p1.x); pData[4*i+3] = SCY(-points[i].p1.y); } break; case BEZ_CURVE_TO: if (0 && (i+1 < numpoints)) { /* real point ?? */ pData[4*i ] = SCX( points[i].p3.x); pData[4*i+1] = SCY(-points[i].p3.y); /* control point (1st from next point) */ pData[4*i+2] = SCX( points[i+1].p1.x); pData[4*i+3] = SCY(-points[i+1].p1.y); } else { /* need to swap these ?? */ /* real point ?? */ pData[4*i ] = SCX( points[i].p2.x); pData[4*i+1] = SCY(-points[i].p2.y); /* control point ?? */ pData[4*i+2] = SCX( points[i].p3.x); pData[4*i+3] = SCY(-points[i].p3.y); } break; } } fwrite_le(pData, sizeof(gint16), numpoints*4, renderer->file); g_free(pData); } #endif /* USE_OUR_DRAW_BEZIER */ static void draw_string(DiaRenderer *self, const char *text, Point *pos, Alignment alignment, Color *colour) { WpgRenderer *renderer = WPG_RENDERER (self); gint16 len; WPGPoint pt; len = strlen(text); DIAG_NOTE(g_message("draw_string(%d) %f,%f %s", len, pos->x, pos->y, text)); if (len < 1) return; /* shouldn't this be handled by Dia's core ? */ renderer->TextStyle.YAlign = 3; /* bottom ??? */ switch (alignment) { case ALIGN_LEFT: renderer->TextStyle.XAlign = 0; break; case ALIGN_CENTER: renderer->TextStyle.XAlign = 1; break; case ALIGN_RIGHT: renderer->TextStyle.XAlign = 2; break; } renderer->TextStyle.Color = LookupColor(renderer, colour); renderer->TextStyle.Angle = 0; renderer->TextStyle.Width = renderer->TextStyle.Height * 0.6; /* ??? */ WriteRecHead(renderer, WPG_TEXTSTYLE, sizeof(WPGTextStyle)); #if 0 fwrite(&renderer->TextStyle, sizeof(WPGTextStyle), 1, renderer->file); #else g_assert(22 == sizeof(WPGTextStyle)); fwrite_le(&renderer->TextStyle.Width, sizeof(guint16), 1, renderer->file); fwrite_le(&renderer->TextStyle.Height, sizeof(guint16), 1, renderer->file); fwrite(&renderer->TextStyle.Reserved, 1, sizeof(renderer->TextStyle.Reserved), renderer->file); fwrite_le(&renderer->TextStyle.Font, sizeof(guint16), 1, renderer->file); fwrite(&renderer->TextStyle.Reserved2, sizeof(guint8), 1, renderer->file); fwrite(&renderer->TextStyle.XAlign, 1, 1, renderer->file); fwrite(&renderer->TextStyle.YAlign, 1, 1, renderer->file); fwrite(&renderer->TextStyle.Color, 1, 1, renderer->file); fwrite_le(&renderer->TextStyle.Angle, sizeof(guint16), 1, renderer->file); #endif pt.x = SCX(pos->x); pt.y = SCY(-pos->y); WriteRecHead(renderer, WPG_TEXT, 3*sizeof(gint16) + len); fwrite_le(&len, sizeof(gint16), 1, renderer->file); fwrite_le(&pt.x, sizeof(guint16), 1, renderer->file); fwrite_le(&pt.y, sizeof(guint16), 1, renderer->file); fwrite(text, 1, len, renderer->file); } static void draw_image(DiaRenderer *self, Point *point, real width, real height, DiaImage image) { WpgRenderer *renderer = WPG_RENDERER (self); WPGBitmap2 bmp; guint8 * pDiaImg = NULL, * pOut = NULL, * pIn = NULL, * p = NULL; guint8 b_1 = 0, b = 0, cnt; int x, y, stride; bmp.Angle = 0; bmp.Left = SCX(point->x); bmp.Top = SCY(-point->y - height); bmp.Right = SCX(point->x + width); bmp.Bottom = SCY(-point->y); bmp.Width = dia_image_width(image); bmp.Height = dia_image_height(image); bmp.Depth = 8; /* maximum allowed */ bmp.Xdpi = 72; /* ??? */ bmp.Ydpi = 72; DIAG_NOTE(g_message("draw_image %fx%f [%d,%d] @%f,%f", width, height, bmp.Width, bmp.Height, point->x, point->y)); pDiaImg = dia_image_rgb_data(image); stride = dia_image_rowstride(image); pOut = g_new(guint8, bmp.Width * bmp.Height * 2); /* space for worst case RLE */ p = pOut; for (y = 0; y < bmp.Height; y++) { /* starting from last line but left to right */ pIn = pDiaImg + stride * (bmp.Height - 1 - y); cnt = 0; /* reset with every line */ for (x = 0; x < bmp.Width; x ++) { /* simple color reduction to default palette */ b = (((CC_LEN - 1) * pIn[0]) / 255) /* red */ + (((CC_LEN - 1) * pIn[1]) / 255) * CC_LEN /* green */ + (((CC_LEN - 1) * pIn[2]) / 255) * CC_LEN * CC_LEN /* blue */ + WPG_NUM_DEF_COLORS; pIn += 3; /* increase read pointer */ if (cnt > 0) { if ((b == b_1) && (cnt < 127)) cnt++; /* increase counter*/ else { *p++ = (cnt | 0x80); /* write run length and value */ *p++ = b_1; cnt = 1; /* reset */ b_1 = b; } } else { b_1 = b; cnt = 1; } } *p++ = (cnt | 0x80); /* write last value(s) */ *p++ = b; } DIAG_NOTE(g_message( "Width x Height: %d RLE: %d", bmp.Width * bmp.Height, p - pOut)); if ((p - pOut) > 32767) { message_warning("WmfRenderer : Bitmap size exceeds blocksize. Ignored."); } else { WriteRecHead(renderer, WPG_BITMAP2, sizeof(WPGBitmap2) + (p - pOut)); #if 0 fwrite(&bmp, sizeof(WPGBitmap2), 1, renderer->file); #else g_assert(20 == sizeof(WPGBitmap2)); fwrite(&bmp, sizeof(guint16), sizeof(WPGBitmap2) / sizeof(guint16), renderer->file); #endif fwrite(pOut, sizeof(guint8), p - pOut, renderer->file); } #if 0 /* RLE diagnose */ { FILE* f; int i, j; f = g_fopen("c:\\temp\\wpg_bmp.raw", "wb"); j = p - pOut; for (i = 0; i < j; i+=2) { for (x = 0; x < (pOut[i] & 0x7F); x++) fwrite(&pOut[i+1], 1, 1, f); } fclose(f); } #endif g_free(pDiaImg); g_free(pOut); } /* gobject boiler plate */ static void wpg_renderer_class_init (WpgRendererClass *klass); static gpointer parent_class = NULL; GType wpg_renderer_get_type (void) { static GType object_type = 0; if (!object_type) { static const GTypeInfo object_info = { sizeof (WpgRendererClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) wpg_renderer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (WpgRenderer), 0, /* n_preallocs */ NULL /* init */ }; object_type = g_type_register_static (DIA_TYPE_RENDERER, "WpgRenderer", &object_info, 0); } return object_type; } static void wpg_renderer_finalize (GObject *object) { WpgRenderer *wpg_renderer = WPG_RENDERER (object); if (wpg_renderer->file) fclose(wpg_renderer->file); wpg_renderer->file = NULL; G_OBJECT_CLASS (parent_class)->finalize (object); } static void wpg_renderer_class_init (WpgRendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->finalize = wpg_renderer_finalize; /* renderer members */ renderer_class->begin_render = begin_render; renderer_class->end_render = end_render; renderer_class->set_linewidth = set_linewidth; renderer_class->set_linecaps = set_linecaps; renderer_class->set_linejoin = set_linejoin; renderer_class->set_linestyle = set_linestyle; renderer_class->set_dashlength = set_dashlength; renderer_class->set_fillstyle = set_fillstyle; renderer_class->set_font = set_font; renderer_class->draw_line = draw_line; renderer_class->fill_polygon = fill_polygon; renderer_class->draw_rect = draw_rect; renderer_class->fill_rect = fill_rect; renderer_class->draw_arc = draw_arc; renderer_class->fill_arc = fill_arc; renderer_class->draw_ellipse = draw_ellipse; renderer_class->fill_ellipse = fill_ellipse; renderer_class->draw_string = draw_string; renderer_class->draw_image = draw_image; /* medium level functions */ renderer_class->draw_rect = draw_rect; renderer_class->draw_polyline = draw_polyline; renderer_class->draw_polygon = draw_polygon; #ifdef USE_OUR_DRAW_BEZIER /* The bezier implementation above does not match, use base class approximation */ renderer_class->draw_bezier = draw_bezier; #endif /* Implemented by base class approximation renderer_class->fill_bezier = fill_bezier; */ } /* dia export funtion */ static void export_data(DiagramData *data, const gchar *filename, const gchar *diafilename, void* user_data) { WpgRenderer *renderer; FILE *file; Rectangle *extent; real width, height; file = g_fopen(filename, "wb"); /* "wb" for binary! */ if (file == NULL) { message_error(_("Can't open output file %s: %s\n"), dia_message_filename(filename), strerror(errno)); return; } renderer = g_object_new (WPG_TYPE_RENDERER, NULL); renderer->file = file; extent = &data->extents; /* use extents */ DIAG_NOTE(g_message("export_data extents %f,%f -> %f,%f", extent->left, extent->top, extent->right, extent->bottom)); width = extent->right - extent->left; height = extent->bottom - extent->top; #if 0 /* extend to use full range */ renderer->Scale = 0.001; if (width > height) while (renderer->Scale * width < 3276.7) renderer->Scale *= 10.0; else while (renderer->Scale * height < 3276.7) renderer->Scale *= 10.0; #else /* scale from Dia's cm to WPU (1/1200 inch) */ renderer->Scale = 1200.0 / 2.54; /* avoid int16 overflow */ if (width > height) while (renderer->Scale * width > 32767) renderer->Scale /= 10.0; else while (renderer->Scale * height > 32767) renderer->Scale /= 10.0; renderer->XOffset = - extent->left; renderer->YOffset = - extent->top; #endif renderer->Box.Width = width * renderer->Scale; renderer->Box.Height = height * renderer->Scale; renderer->Box.Flag = 0; renderer->Box.Version = 0; data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL); g_object_unref(renderer); } #if WPG_WITH_IMPORT /* * Import (under construction) */ static void import_object(DiaRenderer* self, DiagramData *dia, WPG_Type type, int iSize, guchar* pData) { WpgRenderer *renderer = WPG_RENDERER(self); WPGPoint* pts = NULL; gint16* pInt16 = NULL; int iNum = 0; gint32 iPre51 = 0; switch (type) { case WPG_LINE: iNum = 2; pts = (WPGPoint*)pData; break; case WPG_POLYLINE: pInt16 = (gint16*)pData; iNum = pInt16[0]; pts = (WPGPoint*)(pData + sizeof(gint16)); break; case WPG_RECTANGLE: iNum = 2; pts = (WPGPoint*)pData; break; case WPG_POLYGON: pInt16 = (gint16*)pData; iNum = pInt16[0]; pts = (WPGPoint*)(pData + sizeof(gint16)); break; case WPG_ELLIPSE: { WPGEllipse* pEll; pEll = (WPGEllipse*)pData; } break; case WPG_POLYCURVE: iPre51 = *((gint32*)pData); pInt16 = (gint16*)pData; iNum = pInt16[2]; pts = (WPGPoint*)(pData + 3*sizeof(gint16)); DIAG_NOTE(g_message("POLYCURVE Num pts %d Pre51 %d", iNum, iPre51)); break; } /* switch */ DIAG_NOTE(g_message("Type %d Num pts %d Size %d", type, iNum, iSize)); } static gboolean import_data (const gchar *filename, DiagramData *dia, void* user_data) { FILE* f; gboolean bRet; WPGHead8 rh; f = g_fopen(filename, "rb"); if (NULL == f) { message_error(_("Couldn't open: '%s' for reading.\n"), dia_message_filename(filename)); bRet = FALSE; } /* check header */ if (bRet) { WPGFileHead fhead; bRet = ( (1 == fread(&fhead, sizeof(WPGFileHead), 1, f)) && fhead.fid[0] == 255 && fhead.fid[1] == 'W' && fhead.fid[2] == 'P' && fhead.fid[3] == 'C' && (1 == fhead.MajorVersion) && (0 == fhead.MinorVersion)); if (!bRet) message_error(_("File: %s type/version unsupported.\n"), dia_message_filename(filename)); } if (bRet) { int iSize; gint16 i16, iNum16; guint8 i8; WpgRenderer *ren = g_object_new (WPG_TYPE_RENDERER, NULL); ren->pPal = g_new0(WPGColorRGB, 256); DIAG_NOTE(g_message("Parsing: %s ", filename)); do { if (1 == fread(&rh, sizeof(WPGHead8), 1, f)) { if (rh.Size < 0xFF) iSize = rh.Size; else { bRet = (1 == fread(&i16, sizeof(guint16), 1, f)); if (0x8000 & i16) { DIAG_NOTE(g_print("Large Object: hi:lo %04X", (int)i16)); iSize = i16 << 16; /* Reading large objects involves major uglyness. Instead of getting * one size, as implied by "Encyclopedia of Graphics File Formats", * it would require putting together small chunks of data to one large * object. The criteria when to stop isn't absolutely clear. */ iSize = 0; bRet = (1 == fread(&i16, sizeof(guint16), 1, f)); DIAG_NOTE(g_print("Large Object: %d\n", (int)i16)); iSize += i16; #if 1 /* Ignore this large objec part */ fseek(f, iSize, SEEK_CUR); continue; #endif } else iSize = i16; } } else iSize = 0; /* DIAG_NOTE(g_message("Type %d Size %d", rh.Type, iSize)); */ if (iSize > 0) { switch (rh.Type) { case WPG_FILLATTR: bRet = (1 == fread(&ren->FillAttr, sizeof(WPGFillAttr), 1, f)); break; case WPG_LINEATTR: bRet = (1 == fread(&ren->LineAttr, sizeof(WPGLineAttr), 1, f)); break; case WPG_START: bRet = (1 == fread(&ren->Box, sizeof(WPGStartData), 1, f)); break; case WPG_STARTWPG2: /* not sure if this is the right thing to do */ bRet &= (1 == fread(&i8, sizeof(gint8), 1, f)); bRet &= (1 == fread(&i16, sizeof(gint16), 1, f)); DIAG_NOTE(g_message("Ignoring tag WPG_STARTWPG2, Size %d\n Type? %d Size? %d", iSize, (int)i8, i16)); fseek(f, iSize - 3, SEEK_CUR); break; case WPG_LINE: case WPG_POLYLINE: case WPG_RECTANGLE: case WPG_POLYGON: case WPG_POLYCURVE: case WPG_ELLIPSE: { guchar* pData; pData = g_new(guchar, iSize); bRet = (iSize == (int)fread(pData, 1, iSize, f)); import_object(DIA_RENDERER(ren), dia, rh.Type, iSize, pData); g_free(pData); } break; case WPG_COLORMAP: bRet &= (1 == fread(&i16, sizeof(gint16), 1, f)); bRet &= (1 == fread(&iNum16, sizeof(gint16), 1, f)); if (iNum16 != (gint16)((iSize - 2) / sizeof(WPGColorRGB))) g_warning("WpgImporter : colormap size/header mismatch."); if (i16 >= 0 && i16 <= iSize) { bRet &= (iNum16 == (int)fread(&ren->pPal[i16], sizeof(WPGColorRGB), iNum16, f)); } else { g_warning("WpgImporter : start color map index out of range."); bRet &= (iNum16 == (int)fread(ren->pPal, sizeof(WPGColorRGB), iNum16, f)); } break; case WPG_BITMAP2: { WPGBitmap2 bmp; fread(&bmp, sizeof(WPGBitmap2), 1, f); DIAG_NOTE(g_message("Bitmap %dx%d %d bits @%d,%d %d,%d.", bmp.Width, bmp.Height, bmp.Depth, bmp.Left, bmp.Top, bmp.Right, bmp.Bottom)); fseek(f, iSize - sizeof(WPGBitmap2), SEEK_CUR); } break; default: fseek(f, iSize, SEEK_CUR); g_warning ("Unknown Type %d Size %d.", (int)rh.Type, iSize); } /* switch */ } /* if iSize */ else { if (WPG_END != rh.Type) g_warning("Size 0 on Type %d, expecting WPG_END\n", (int)rh.Type); } } while ((iSize > 0) && (bRet)); if (!bRet) g_warning("WpgImporter : unexpected eof. Type %d, Size %d.\n", rh.Type, iSize); if (ren->pPal) g_free(ren->pPal); g_object_unref (ren); } /* bRet */ if (f) fclose(f); return bRet; } #endif /* WPG_WITH_IMPORT */ static const gchar *extensions[] = { "wpg", NULL }; static DiaExportFilter my_export_filter = { N_("WPG"), extensions, export_data }; #if WPG_WITH_IMPORT DiaImportFilter my_import_filter = { N_("WPG"), extensions, import_data }; #endif /* WPG_WITH_IMPORT */ /* --- dia plug-in interface --- */ static gboolean _plugin_can_unload (PluginInfo *info) { return TRUE; } static void _plugin_unload (PluginInfo *info) { filter_unregister_export(&my_export_filter); #if WPG_WITH_IMPORT filter_unregister_import(&my_import_filter); #endif } DIA_PLUGIN_CHECK_INIT PluginInitResult dia_plugin_init(PluginInfo *info) { if (!dia_plugin_info_init(info, "WPG", _("WordPerfect Graphics export filter"), _plugin_can_unload, _plugin_unload)) return DIA_PLUGIN_INIT_ERROR; filter_register_export(&my_export_filter); #if WPG_WITH_IMPORT filter_register_import(&my_import_filter); #endif return DIA_PLUGIN_INIT_OK; }