]> granicus.if.org Git - graphviz/commitdiff
Visio VDX render plugin
authorglenlow <devnull@localhost>
Tue, 31 Mar 2009 13:14:48 +0000 (13:14 +0000)
committerglenlow <devnull@localhost>
Tue, 31 Mar 2009 13:14:48 +0000 (13:14 +0000)
plugin/visio/VisioGraphic.cpp [new file with mode: 0644]
plugin/visio/VisioGraphic.h [new file with mode: 0644]
plugin/visio/VisioRender.cpp [new file with mode: 0644]
plugin/visio/VisioRender.h [new file with mode: 0644]
plugin/visio/VisioText.cpp [new file with mode: 0644]

diff --git a/plugin/visio/VisioGraphic.cpp b/plugin/visio/VisioGraphic.cpp
new file mode 100644 (file)
index 0000000..ff3a193
--- /dev/null
@@ -0,0 +1,552 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+ *      This software is part of the graphviz package      *
+ *                http://www.graphviz.org/                 *
+ *                                                         *
+ *            Copyright (c) 1994-2004 AT&T Corp.           *
+ *                and is licensed under the                *
+ *            Common Public License, Version 1.0           *
+ *                      by AT&T Corp.                      *
+ *                                                         *
+ *        Information and Software Systems Research        *
+ *              AT&T Research, Florham Park NJ             *
+ **********************************************************/
+
+#include <algorithm>
+
+#include "VisioGraphic.h"
+
+#include "gvcjob.h"
+#include "gvio.h"
+
+namespace Visio
+{
+       static const float INCHES_PER_POINT = 1.0 / 72.0;
+       
+       Fill::Fill(unsigned char red, unsigned char green, unsigned char blue, double transparency):
+               _red(red),
+               _green(green),
+               _blue(blue),
+               _transparency(transparency)
+       {
+       }
+       
+       void Fill::Print(GVJ_t* job) const
+       {
+               gvputs(job, "<Fill>\n");
+               gvprintf(job, "<FillForegnd>#%02X%02X%02X</FillForegnd>\n", _red, _green, _blue);       /* VDX uses hex colors */
+               gvprintf(job, "<FillForegndTrans>%f</FillForegndTrans>\n", _transparency); 
+               gvputs(job, "</Fill>\n");
+       }
+       
+       Line::Line(double weight, unsigned char red, unsigned char green, unsigned char blue, unsigned int pattern, unsigned int beginArrow, unsigned int endArrow):
+               _weight(weight),
+               _red(red),
+               _green(green),
+               _blue(blue),
+               _pattern(pattern),
+               _beginArrow(beginArrow),
+               _endArrow(endArrow)
+       {
+       }
+       
+       void Line::Print(GVJ_t* job) const
+       {
+               gvputs(job, "<Line>\n");
+               gvprintf(job, "<LineWeight>%f</LineWeight>\n", _weight * job->scale.x * INCHES_PER_POINT);      /* scale line weight, VDX uses inches */
+               gvprintf(job, "<LineColor>#%02X%02X%02X</LineColor>\n", _red, _green, _blue);   /* VDX uses hex colors */
+               if (_pattern)
+                       gvprintf(job, "<LinePattern>%d</LinePattern>\n", _pattern);
+               if (_beginArrow)
+                       gvprintf(job, "<BeginArrow>%d</BeginArrow>\n", _beginArrow);
+               if (_endArrow)
+                       gvprintf(job, "<EndArrow>%d</EndArrow>\n", _endArrow);  
+               gvputs(job, "</Line>\n");
+       }
+
+       Ellipse::Ellipse(pointf* points, bool filled):
+               _filled(filled)
+       {
+               _points[0] = points[0];
+               _points[1] = points[1];
+       }
+       
+       void Ellipse::Print(GVJ_t* job, pointf first, pointf last) const
+       {
+               gvputs(job, "<Geom>\n");
+               if (!_filled)
+                       gvputs(job, "<NoFill>1</NoFill>\n");    /* omit fill? */        
+               gvputs(job, "<MoveTo><X F='Width*0' /><Y F='Height*0.5' /></MoveTo>\n");
+               gvputs(job, "<EllipticalArcTo><X F='Width*1' /><Y F='Height*0.5' /><A F='Width*0.5' /><B F='Height*1' /><C>0</C><D F='Width/Height*1' /></EllipticalArcTo>\n"); /* semi ellipse */
+               gvputs(job, "<EllipticalArcTo><X F='Geometry1.X1' /><Y F='Geometry1.Y1' /><A F='Width*0.5' /><B F='Height*0' /><C>0</C><D F='Width/Height*1' /></EllipticalArcTo>\n");  /* semi ellipse */
+               gvputs(job, "</Geom>\n");
+       }
+       
+       boxf Ellipse::GetBounds() const
+       {
+               /* point[0] is center, point[1] is one corner */
+               boxf bounds;
+               bounds.LL.x = _points[0].x + _points[0].x - _points[1].x;
+               bounds.LL.y = _points[0].y + _points[0].y - _points[1].y;
+               bounds.UR.x = _points[1].x;
+               bounds.UR.y = _points[1].y;
+               return bounds;
+       }
+       
+       pointf Ellipse::GetFirst() const
+       {
+               return _points[0];
+       }
+       
+       pointf Ellipse::GetLast() const
+       {
+               return _points[1];
+       }
+       
+       pointf Ellipse::GetCenter() const
+       {
+               /* only called for edges, so return a null point */
+               pointf center = {0, 0};
+               return center;
+       }
+       
+       Path::Path(pointf* points, int pointCount)
+       {
+               /* copy over the points */
+               _points = (pointf*)malloc(sizeof(_points[0]) * pointCount);
+               memcpy(_points, points, sizeof(_points[0]) * pointCount);
+               _pointCount = pointCount;
+       }
+       
+       Path::~Path()
+       {
+               /* since we copied, we need to free */
+               free(_points);
+       }
+       
+       boxf Path::GetBounds() const
+       {
+               boxf bounds;
+               if (_points && _pointCount > 0)
+               {
+                       /* lower left is the minimal point, upper right is maximal point */
+                       bounds.LL.x = bounds.UR.x = _points[0].x;
+                       bounds.LL.y = bounds.UR.y = _points[0].y;
+                       for (int i = 1; i < _pointCount; ++i)
+                       {
+                               if (bounds.LL.x > _points[i].x)
+                                       bounds.LL.x = _points[i].x;
+                               if (bounds.LL.y > _points[i].y)
+                                       bounds.LL.y = _points[i].y;
+                               if (bounds.UR.x < _points[i].x)
+                                       bounds.UR.x = _points[i].x;
+                               if (bounds.UR.y < _points[i].y)
+                                       bounds.UR.y = _points[i].y;
+                       }
+               }
+               else
+               {
+                       /* no points, return null bounds */
+                       bounds.LL.x = bounds.UR.x = 0.0;
+                       bounds.LL.y = bounds.UR.y = 0.0;                        
+               }
+               return bounds;
+       }
+       
+       pointf Path::GetFirst() const
+       {
+               return _points[0];
+       }
+       
+       pointf Path::GetLast() const
+       {
+               return _points[_pointCount - 1];
+       }
+       
+       
+       Bezier::Bezier(pointf* points, int pointCount, bool filled):
+               Path(points, pointCount),
+               _filled(filled)
+       {
+       }
+       
+       pointf Bezier::GetCenter() const
+       {
+               if (_pointCount >= 4 && _pointCount % 2 == 0)
+               {
+                       /* the central control polygon for the bezier curve */
+                       pointf p0 = _points[_pointCount / 2 - 2];
+                       pointf p1 = _points[_pointCount / 2 - 1];
+                       pointf p2 = _points[_pointCount / 2];
+                       pointf p3 = _points[_pointCount / 2 + 1];
+                       
+                       /* use de Casteljou's algorithm to get a midpoint */
+                       pointf center;
+                       center.x = 0.125 * p0.x + 0.375 * p1.x + 0.375 * p2.x + 0.125 * p3.x;
+                       center.y = 0.125 * p0.y + 0.375 * p1.y + 0.375 * p2.y + 0.125 * p3.y;
+                       return center;
+               }
+               else
+                       /* just return the middle point */
+                       return _points[_pointCount / 2];
+       }
+       
+       void Bezier::Print(GVJ_t* job, pointf first, pointf last) const
+       {
+               gvputs(job, "<Geom>\n");
+               if (!_filled)
+                       gvputs(job, "<NoFill>1</NoFill>\n");            
+               if (_pointCount > 0)
+               {
+                       double xscale = 1.0 / (last.x - first.x);
+                       double yscale = 1.0 / (last.y - first.y);
+                       if (isinf(xscale))
+                               xscale = 0.0;
+                       if (isinf(yscale))
+                               yscale = 0.0;
+                               
+                       gvputs(job, "<MoveTo>");
+                       gvprintf(job, "<X F='Width*%f' />", (_points[0].x - first.x) * xscale);
+                       gvprintf(job, "<Y F='Height*%f' />", (_points[0].y - first.y) * yscale);
+                       gvputs(job, "</MoveTo>\n");
+                       
+                       /* convert Graphviz cubic bezier into VDX NURBS curve: */
+                       /* NURBS control points == bezier control points */
+                       /* NURBS order == bezier order == 3 */
+                       /* NURBS knot vector == { 0, 0, 0, 0,  1, 2 ... } */
+                       
+                       gvputs(job, "<NURBSTo>");
+                       
+                       /* Ctl[P-1].X */
+                       gvprintf(job, "<X F='Width*%f'/>", (_points[_pointCount - 1].x - first.x) * xscale);
+                       
+                       /* Ctl[P-1].Y */
+                       gvprintf(job, "<Y F='Height*%f'/>", (_points[_pointCount - 1].y - first.y) * yscale);
+                       
+                       /* Knot[P-1] */
+                       gvprintf(job, "<A>%d</A>", std::max(_pointCount - 4, 0));
+                       
+                       /* Ctl[P-1].Weight */
+                       gvputs(job, "<B>1</B>");        
+                       
+                       /* Knot[0] */
+                       gvputs(job, "<C>0</C>");
+                       
+                       /* Weight[0] */
+                       gvputs(job, "<D>1</D>");        
+                       
+                       /* Knot[P], Degree, XType, YType */
+                       gvprintf(job, "<E F='NURBS(%d, 3, 0, 0", std::max(_pointCount - 3, 0));                         
+                       for (int i = 1; i < _pointCount; ++i)
+                       /* Ctl[i].X, Ctl[i].Y, Knot[i], Ctl[i].Weight */
+                               gvprintf(job, ", %f, %f, %d, 1",                                        
+                                                (_points[i].x - first.x) * xscale,
+                                                (_points[i].y - first.y) * yscale,
+                                                std::max(i - 3, 0));   
+                       gvputs(job, ")'/>");
+                       
+                       gvputs(job, "</NURBSTo>\n");
+               }
+               gvputs(job, "</Geom>\n");
+       }
+       
+       Polygon::Polygon(pointf* points, int pointCount, bool filled):
+               Path(points, pointCount),
+               _filled(filled)
+       {
+       }
+       
+       pointf Polygon::GetCenter() const
+       {
+               /* should not get called, return a null point */
+               pointf center = {0, 0};
+               return center;
+       }
+       
+       void Polygon::Print(GVJ_t* job, pointf first, pointf last) const
+       {
+               gvputs(job, "<Geom>\n");
+               if (!_filled)
+                       gvputs(job, "<NoFill>1</NoFill>\n");            
+               if (_pointCount > 0)
+               {
+                       /* compute scale. if infinite, scale by 0 instead */
+                       double xscale = 1.0 / (last.x - first.x);
+                       double yscale = 1.0 / (last.y - first.y);
+                       if (isinf(xscale))
+                               xscale = 0.0;
+                       if (isinf(yscale))
+                               yscale = 0.0;
+                                       
+                       gvputs(job, "<MoveTo>");
+                       gvprintf(job, "<X F='Width*%f' />", (_points[0].x - first.x) * xscale);
+                       gvprintf(job, "<Y F='Height*%f' />", (_points[0].y - first.y) * yscale);
+                       gvputs(job, "</MoveTo>\n");
+                       
+                       if (_pointCount == 1)
+                       {
+                               /* single point, use VDX LineTo */
+                               gvputs(job, "<LineTo>");
+                               gvprintf(job, "<X F='Width*%f' />", (_points[0].x - first.x) * xscale);
+                               gvprintf(job, "<Y F='Height*%f' />", (_points[0].y - first.y) * yscale);
+                               gvputs(job, "</LineTo>\n");
+                       }
+                       else
+                       {
+                               /* multiple points, use VDX PolylineTo */
+                               gvputs(job, "<PolylineTo>");
+                               gvprintf(job, "<X F='Width*%f' />", (_points[0].x - first.x) * xscale);
+                               gvprintf(job, "<Y F='Height*%f' />", (_points[0].y - first.y) * yscale);
+                               gvputs(job, "<A F='POLYLINE(0, 0");
+                               for (int i = 1; i < _pointCount; ++i)
+                                       gvprintf(job, ", %f, %f", (_points[i].x - first.x) * xscale, (_points[i].y - first.y) * yscale);
+                               gvputs(job, ")' />");
+                               gvputs(job, "</PolylineTo>\n");
+                       }
+               }
+               gvputs(job, "</Geom>\n");
+       }
+
+       Polyline::Polyline(pointf* points, int pointCount):
+               Path(points, pointCount)
+       {
+       }
+       
+       pointf Polyline::GetCenter() const
+       {
+               if (_pointCount >= 2 && _pointCount % 2 == 0)
+               {
+                       /* the center two points */
+                       pointf p0 = _points[_pointCount / 2 - 1];
+                       pointf p1 = _points[_pointCount / 2];
+                       
+                       /* take the midpoint */
+                       pointf center;
+                       center.x = (p0.x + p1.x) * 0.5;
+                       center.y = (p0.y + p1.y) * 0.5;
+                       return center;
+               }
+               else
+                       /* just return the middle point */
+                       return _points[_pointCount / 2];
+       }
+       
+       void Polyline::Print(GVJ_t* job, pointf first, pointf last) const
+       {
+               gvputs(job, "<Geom>\n");
+               if (_pointCount > 0)
+               {
+                       /* compute scale. if infinite, scale by 0 instead */
+                       double xscale = 1.0 / (last.x - first.x);
+                       double yscale = 1.0 / (last.y - first.y);
+                       if (isinf(xscale))
+                               xscale = 0.0;
+                       if (isinf(yscale))
+                               yscale = 0.0;
+                                       
+                       gvputs(job, "<MoveTo>");
+                       gvprintf(job, "<X F='Width*%f' />", (_points[0].x - first.x) * xscale);
+                       gvprintf(job, "<Y F='Height*%f' />", (_points[0].y - first.y) * yscale);
+                       gvputs(job, "</MoveTo>\n");
+                       
+                       if (_pointCount == 2)
+                       {
+                               /* single point, use VDX LineTo */
+                               gvputs(job, "<LineTo>");
+                               gvprintf(job, "<X F='Width*%f' />", (_points[1].x - first.x) * xscale);
+                               gvprintf(job, "<Y F='Height*%f' />", (_points[1].y - first.y) * yscale);
+                               gvputs(job, "</LineTo>\n");
+                       }
+                       else
+                       {
+                               /* multiple points, use VDX PolylineTo */
+                               gvputs(job, "<PolylineTo>");
+                               gvprintf(job, "<X F='Width*%f' />", (_points[_pointCount - 1].x - first.x) * xscale);
+                               gvprintf(job, "<Y F='Height*%f' />", (_points[_pointCount - 1].y - first.y) * yscale);
+                               gvputs(job, "<A F='POLYLINE(0, 0");
+                               for (int i = 1; i < _pointCount - 1; ++i)
+                                       gvprintf(job, ", %f, %f", (_points[i].x - first.x) * xscale, (_points[i].y - first.y) * yscale);
+                               gvputs(job, ")' />");
+                               gvputs(job, "</PolylineTo>\n");
+                       }
+               }
+               gvputs(job, "</Geom>\n");
+       }
+
+       Graphic* Graphic::CreateEllipse(GVJ_t* job, pointf* A, bool filled)
+       {
+               unsigned int pattern;
+               switch (job->obj->pen)
+               {
+                       case PEN_DASHED:
+                               pattern = 2;
+                               break;
+                       case PEN_DOTTED:
+                               pattern = 3;
+                               break;
+                       default:
+                               pattern = 1;
+                               break;
+               }
+               return new Graphic(
+                       new Line(
+                               job->obj->penwidth,
+                               job->obj->pencolor.u.rgba[0],
+                               job->obj->pencolor.u.rgba[1],
+                               job->obj->pencolor.u.rgba[2],
+                               pattern),
+                       filled ? new Fill(
+                               job->obj->fillcolor.u.rgba[0],
+                               job->obj->fillcolor.u.rgba[1],
+                               job->obj->fillcolor.u.rgba[2],
+                               (255 - job->obj->fillcolor.u.rgba[3]) / 255.0) : NULL,  /* Graphviz alpha (00 - FF) to VDX transparency (1.0 - 0.0) */
+                       new Ellipse(A, filled));
+       }
+       
+       Graphic* Graphic::CreateBezier(GVJ_t* job, pointf* A, int n, bool arrow_at_start, bool arrow_at_end, bool filled)
+       {
+               unsigned int pattern;
+               switch (job->obj->pen)
+               {
+                       case PEN_DASHED:
+                               pattern = 2;
+                               break;
+                       case PEN_DOTTED:
+                               pattern = 3;
+                               break;
+                       default:
+                               pattern = 1;
+                               break;
+               }
+               return new Graphic(
+                       new Line(
+                               job->obj->penwidth,
+                               job->obj->pencolor.u.rgba[0],
+                               job->obj->pencolor.u.rgba[1],
+                               job->obj->pencolor.u.rgba[2],
+                               pattern,
+                               arrow_at_start ? 2 : 0,         /* VDX arrow type 2 == filled solid */
+                               arrow_at_end ? 2 : 0),          /* VDX arrow type 2 == filled solid */
+                       filled ? new Fill(
+                               job->obj->fillcolor.u.rgba[0],
+                               job->obj->fillcolor.u.rgba[1],
+                               job->obj->fillcolor.u.rgba[2],
+                               (255 - job->obj->fillcolor.u.rgba[3]) / 255.0) : NULL,  /* Graphviz alpha (00 - FF) to VDX transparency (1.0 - 0.0) */
+                       new Bezier(
+                               A,
+                               n,
+                               filled));
+       }
+       
+       Graphic* Graphic::CreatePolygon(GVJ_t* job, pointf* A, int n, bool filled)
+       {
+               unsigned int pattern;
+               switch (job->obj->pen)
+               {
+                       case PEN_DASHED:
+                               pattern = 2;
+                               break;
+                       case PEN_DOTTED:
+                               pattern = 3;
+                               break;
+                       default:
+                               pattern = 1;
+                               break;
+               }
+               return new Graphic(
+                       new Line(
+                               job->obj->penwidth,
+                               job->obj->pencolor.u.rgba[0],
+                               job->obj->pencolor.u.rgba[1],
+                               job->obj->pencolor.u.rgba[2],
+                               pattern),
+                       filled ? new Fill(job->obj->fillcolor.u.rgba[0],
+                               job->obj->fillcolor.u.rgba[1],
+                               job->obj->fillcolor.u.rgba[2],
+                               (255 - job->obj->fillcolor.u.rgba[3]) / 255.0) : NULL,  /* Graphviz alpha (00 - FF) to VDX transparency (1.0 - 0.0) */
+                       new Polygon(
+                               A,
+                               n,
+                               filled));
+       }
+       
+       Graphic* Graphic::CreatePolyline(GVJ_t* job, pointf* A, int n)
+       {
+               unsigned int pattern;
+               switch (job->obj->pen)
+               {
+                       case PEN_DASHED:
+                               pattern = 2;
+                               break;
+                       case PEN_DOTTED:
+                               pattern = 3;
+                               break;
+                       default:
+                               pattern = 1;
+                               break;
+               }
+               return new Graphic(
+                       new Line(
+                               job->obj->penwidth,
+                               job->obj->pencolor.u.rgba[0],
+                               job->obj->pencolor.u.rgba[1],
+                               job->obj->pencolor.u.rgba[2],
+                               pattern),
+                       NULL,           /* polylines have no fill */
+                       new Polyline(
+                               A,
+                               n));
+       }
+       
+       Graphic::Graphic(Line* line, Fill* fill, Geom* geom):
+               _line(line),
+               _fill(fill),
+               _geom(geom)
+       {
+       }
+       
+       Graphic::~Graphic()
+       {
+               if (_line)
+                       delete _line;
+               if (_fill)
+                       delete _fill;
+               if (_geom)
+                       delete _geom;
+       }
+
+       boxf Graphic::GetBounds() const
+       {
+               return _geom->GetBounds();
+       }
+       
+       pointf Graphic::GetFirst() const
+       {
+               return _geom->GetFirst();
+       }
+       
+       pointf Graphic::GetLast() const
+       {
+               return _geom->GetLast();
+       }
+
+       pointf Graphic::GetCenter() const
+       {
+               return _geom->GetCenter();
+       }
+       
+       void Graphic::Print(GVJ_t* job, pointf first, pointf last) const
+       {
+               if (_line)
+                       _line->Print(job);
+               if (_fill)
+                       _fill->Print(job);
+               if (_geom)
+                       _geom->Print(job, first, last);
+               
+       }
+       
+
+}
\ No newline at end of file
diff --git a/plugin/visio/VisioGraphic.h b/plugin/visio/VisioGraphic.h
new file mode 100644 (file)
index 0000000..d8512c6
--- /dev/null
@@ -0,0 +1,170 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+ *      This software is part of the graphviz package      *
+ *                http://www.graphviz.org/                 *
+ *                                                         *
+ *            Copyright (c) 1994-2004 AT&T Corp.           *
+ *                and is licensed under the                *
+ *            Common Public License, Version 1.0           *
+ *                      by AT&T Corp.                      *
+ *                                                         *
+ *        Information and Software Systems Research        *
+ *              AT&T Research, Florham Park NJ             *
+ **********************************************************/
+
+#ifndef VISIOGRAPHIC_H
+#define VISIOGRAPHIC_H
+
+#include "types.h"
+
+namespace Visio
+{
+       /* Fill VDX element */
+       
+       class Fill
+       {
+       public:
+               Fill(unsigned char red, unsigned char green, unsigned char blue, double transparency);
+               
+               /* output the fill */
+               void Print(GVJ_t* job) const;
+               
+       private:
+               unsigned char _red;
+               unsigned char _green;
+               unsigned char _blue;
+               double _transparency;   /* 0.0 == opaque, 1.0 == transparent */
+       };
+
+       /* Line VDX element */
+       
+       class Line
+       {
+       public:
+               Line(double weight, unsigned char red, unsigned char green, unsigned char blue, unsigned int pattern, unsigned int beginArrow = 0, unsigned int endArrow = 0);
+               
+               /* output the line */
+               void Print(GVJ_t* job) const;
+               
+       private:
+               double _weight;
+               unsigned char _red;
+               unsigned char _green;
+               unsigned char _blue;
+               unsigned int _pattern;          /* solid == 1, dashed == 2, dotted == 3 etc. */
+               unsigned int _beginArrow;       /* arrow type e.g. 2 is filled arrow head */
+               unsigned int _endArrow;         /* arrow type e.g. 2 is filled arrow head */
+       };
+       
+       /* Geom VDX element */
+       
+       class Geom
+       {
+       public:
+               virtual boxf GetBounds() const = 0;             /* bounding box -- used by node logic */
+               virtual pointf GetFirst() const = 0;    /* first point -- used by edge logic */
+               virtual pointf GetLast() const = 0;             /* last point -- used by edge logic */
+               virtual pointf GetCenter() const = 0;   /* midpoint of the path -- used by text logic */
+               
+               /* given first (lower left) and last points (upper right), output the geometry */ 
+               virtual void Print(GVJ_t* job, pointf first, pointf last) const = 0;
+       };
+       
+       class Ellipse: public Geom
+       {
+       public:
+               Ellipse(pointf* points, bool filled);
+               void Print(GVJ_t* job, pointf first, pointf last) const;
+               
+               boxf GetBounds() const;
+               pointf GetFirst() const;
+               pointf GetLast() const;
+               pointf GetCenter() const;
+       
+       private:
+               bool _filled;
+               pointf _points[2];
+       };
+       
+       class Path: public Geom
+       {
+       public:
+               Path(pointf* points, int pointCount);
+               ~Path();
+               
+               boxf GetBounds() const;
+               pointf GetFirst() const;
+               pointf GetLast() const;
+               
+       protected:
+               pointf* _points;
+               int _pointCount;
+       };
+               
+       class Bezier: public Path
+       {
+       public:
+               Bezier(pointf* points, int pointCount, bool filled);
+               
+               pointf GetCenter() const;
+               
+               void Print(GVJ_t* job, pointf first, pointf last) const;
+               
+       private:
+               bool _filled;
+       };
+       
+       class Polygon: public Path
+       {
+       public:
+               Polygon(pointf* points, int pointCount, bool filled);
+               
+               pointf GetCenter() const;
+
+               void Print(GVJ_t* job, pointf first, pointf last) const;
+               
+       private:
+               bool _filled;
+       };
+
+       class Polyline: public Path
+       {
+       public:
+               Polyline(pointf* points, int pointCount);
+               
+               pointf GetCenter() const;
+
+               void Print(GVJ_t* job, pointf first, pointf last) const;
+       };
+       
+       /* Line, Fill and Geom details for each Graphviz graphic */
+       
+       class Graphic
+       {
+       public:
+               static Graphic* CreateEllipse(GVJ_t* job, pointf* A, bool filled);
+               static Graphic* CreateBezier(GVJ_t* job, pointf* A, int n, bool arrow_at_start, bool arrow_at_end, bool filled);
+               static Graphic* CreatePolygon(GVJ_t* job, pointf* A, int n, bool filled);
+               static Graphic* CreatePolyline(GVJ_t* job, pointf* A, int n);
+               
+               ~Graphic();
+
+               boxf GetBounds() const;
+               pointf GetFirst() const;
+               pointf GetLast() const;
+               pointf GetCenter() const;
+
+               void Print(GVJ_t* job, pointf first, pointf last) const;
+               
+       private:
+               Graphic(Line* line, Fill* fill, Geom* geom);
+               
+               Line* _line;
+               Fill* _fill;
+               Geom* _geom;
+       };
+}
+
+#endif
\ No newline at end of file
diff --git a/plugin/visio/VisioRender.cpp b/plugin/visio/VisioRender.cpp
new file mode 100644 (file)
index 0000000..8a0874c
--- /dev/null
@@ -0,0 +1,453 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+ *      This software is part of the graphviz package      *
+ *                http://www.graphviz.org/                 *
+ *                                                         *
+ *            Copyright (c) 1994-2004 AT&T Corp.           *
+ *                and is licensed under the                *
+ *            Common Public License, Version 1.0           *
+ *                      by AT&T Corp.                      *
+ *                                                         *
+ *        Information and Software Systems Research        *
+ *              AT&T Research, Florham Park NJ             *
+ **********************************************************/
+
+#include "VisioRender.h"
+
+#include "gvcjob.h"
+#include "gvio.h"
+
+namespace Visio
+{
+       static const float INCHES_PER_POINT = 1.0 / 72.0;
+       static const float ZERO_ADJUST = 0.125;
+       
+       Render::Render():
+               _pageId(0),
+               _shapeId(0),
+               _hyperlinkId(0),
+               _inComponent(false),
+               _graphics(),
+               _texts(),
+               _hyperlinks(),
+               _nodeIds()
+       {
+       }
+
+       Render::~Render()
+       {
+       }
+
+       void Render::BeginGraph(GVJ_t* job)
+       {
+               gvputs(job, "<VisioDocument xmlns='http://schemas.microsoft.com/visio/2003/core'>\n");
+               gvputs(job, "<Pages>\n");
+       }
+
+       void Render::EndGraph(GVJ_t* job)
+       {
+               gvputs(job, "</Pages>\n");
+               gvputs(job, "</VisioDocument>\n");
+       }
+
+       void Render::BeginPage(GVJ_t* job)
+       {
+               gvprintf(job, "<Page ID='%d'>\n", ++_pageId);
+               
+               gvputs(job, "<PageSheet>\n");
+               gvputs(job, "<PageProps>\n");
+               gvprintf(job, "<PageWidth>%f</PageWidth>\n", job->width * INCHES_PER_POINT);
+               gvprintf(job, "<PageHeight>%f</PageHeight>\n", job->height * INCHES_PER_POINT);
+               gvputs(job, "</PageProps>\n");
+               gvputs(job, "</PageSheet>\n");
+               
+               gvputs(job, "<Shapes>");
+       }
+
+       void Render::EndPage(GVJ_t* job)
+       {
+               gvputs(job, "</Shapes>\n");
+               gvputs(job, "</Page>\n");
+       }
+
+       void Render::BeginNode(GVJ_t* job)
+       {
+               _inComponent = true;
+               ClearGraphicsAndTexts();
+       }
+
+       void Render::EndNode(GVJ_t* job)
+       {
+               _inComponent = false;
+               
+               unsigned int outerShapeId = 0;
+               switch (_graphics.size())
+               {
+               case 0:
+                       /* no graphics to render */
+                       break;
+               case 1:
+                       /* single graphic to render, output as top level shape */
+                       PrintOuterShape(job, _graphics[0]);
+                       outerShapeId = _shapeId;
+                       break;
+               default:
+                       /* multiple graphics to render, output each as a subshape of a group */
+                               
+                       /* calculate group bounds */
+                       boxf outerBounds = _graphics[0]->GetBounds();
+                       for (Graphics::const_iterator nextGraphic = _graphics.begin() + 1, lastGraphic = _graphics.end();
+                                nextGraphic != lastGraphic;
+                                ++nextGraphic)
+                       {
+                               boxf innerBounds = (*nextGraphic)->GetBounds();
+                               if (outerBounds.LL.x > innerBounds.LL.x)
+                                       outerBounds.LL.x = innerBounds.LL.x;
+                               if (outerBounds.LL.y > innerBounds.LL.y)
+                                       outerBounds.LL.y = innerBounds.LL.y;
+                               if (outerBounds.UR.x < innerBounds.UR.x)
+                                       outerBounds.UR.x = innerBounds.UR.x;
+                               if (outerBounds.UR.y < innerBounds.UR.y)
+                                       outerBounds.UR.y = innerBounds.UR.y;
+                       }
+                       
+                       gvprintf(job, "<Shape ID='%d' Type='Group'>\n", ++_shapeId);
+                       outerShapeId = _shapeId;                        
+                       
+                       gvputs(job, "<XForm>\n");
+                       gvprintf(job, "<PinX>%f</PinX>\n", (outerBounds.LL.x + outerBounds.UR.x) * 0.5 * INCHES_PER_POINT);
+                       gvprintf(job, "<PinY>%f</PinY>\n", (outerBounds.LL.y + outerBounds.UR.y) * 0.5 * INCHES_PER_POINT);
+                       gvprintf(job, "<Width>%f</Width>\n", (outerBounds.UR.x - outerBounds.LL.x) * INCHES_PER_POINT);
+                       gvprintf(job, "<Height>%f</Height>\n", (outerBounds.UR.y - outerBounds.LL.y) * INCHES_PER_POINT);
+                       gvputs(job, "</XForm>\n");
+
+                       /* output Hyperlink */
+                       PrintHyperlinks(job);
+                               
+                       /* output Para, Char */
+                       PrintTexts(job);
+               
+                       /* output subshapes */
+                       gvputs(job, "<Shapes>\n");
+                       for (Graphics::const_iterator nextGraphic = _graphics.begin(), lastGraphic = _graphics.end(); nextGraphic != lastGraphic; ++nextGraphic)
+                               PrintInnerShape(job, *nextGraphic, outerShapeId, outerBounds);
+                       gvputs(job, "</Shapes>\n");
+                               
+                       gvputs(job, "</Shape>\n");
+                       break;
+               }
+               
+               /* save node id for edge logic */
+               if (outerShapeId)
+                       _nodeIds[job->obj->u.n] = outerShapeId;
+               ClearGraphicsAndTexts();
+       }
+
+       void Render::BeginEdge(GVJ_t* job)
+       {
+               _inComponent = true;
+               ClearGraphicsAndTexts();
+       }
+
+       void Render::EndEdge(GVJ_t* job)
+       {
+               _inComponent = false;
+               
+               if (_graphics.size() > 0)
+               {
+                       /* get previously saved ids for tail and head node */
+                       NodeIds::const_iterator beginId = _nodeIds.find(job->obj->u.e->tail);
+                       NodeIds::const_iterator endId = _nodeIds.find(job->obj->u.e->head);
+                       
+                       /* output first shape as an edge shape */
+                       PrintEdgeShape(job, _graphics[0], beginId == _nodeIds.end() ? 0 : beginId->second, endId == _nodeIds.end() ? 0 : endId->second);
+               }
+               ClearGraphicsAndTexts();
+       }
+
+       void Render::AddEllipse(GVJ_t* job, pointf* A, bool filled)
+       {
+               AddGraphic(job, Graphic::CreateEllipse(job, A, filled));
+       }
+
+       void Render::AddBezier(GVJ_t* job, pointf* A, int n, bool arrow_at_start, bool arrow_at_end, bool filled)
+       {
+               AddGraphic(job, Graphic::CreateBezier(job, A, n, arrow_at_start, arrow_at_end, filled));
+       }
+
+       void Render::AddPolygon(GVJ_t* job, pointf* A, int n, bool filled)
+       {
+               AddGraphic(job, Graphic::CreatePolygon(job, A, n, filled));
+       }
+
+       void Render::AddPolyline(GVJ_t* job, pointf* A, int n)
+       {
+               AddGraphic(job, Graphic::CreatePolyline(job, A, n));
+       }
+       
+       void Render::AddText(GVJ_t* job, pointf p, textpara_t *para)
+       {
+               AddText(job, Text::CreateText(job, p, para));
+       }
+
+       void Render::AddAnchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id)
+       {
+               AddHyperlink(job, Hyperlink::CreateHyperlink(job, url, tooltip, target, id));
+       }
+       
+       void Render::ClearGraphicsAndTexts()
+       {
+               /* clear graphics */
+               for (Graphics::iterator nextGraphic = _graphics.begin(), lastGraphic = _graphics.end(); nextGraphic != lastGraphic; ++nextGraphic)
+                       delete *nextGraphic;
+               _graphics.clear();
+               
+               /* clear texts */
+               for (Texts::iterator nextText = _texts.begin(), lastText = _texts.end(); nextText != lastText; ++nextText)
+                       delete *nextText;
+               _texts.clear();
+
+               /* clear hyperlinks */
+               for (Hyperlinks::iterator nextHyperlink = _hyperlinks.begin(), lastHyperlink = _hyperlinks.end(); nextHyperlink != lastHyperlink; ++nextHyperlink)
+                       delete *nextHyperlink;
+               _hyperlinks.clear();
+       }
+       
+       void Render::AddGraphic(GVJ_t* job, Graphic* graphic)
+       {
+               if (_inComponent)
+                       /* if in component, accumulate for end node/edge */
+                       _graphics.push_back(graphic);
+               else
+                       /* if outside, output immediately */
+                       PrintOuterShape(job, graphic);          
+       }
+
+       void Render::AddText(GVJ_t* job, Text* text)
+       {
+               /* if in component, accumulate for end node/edge */
+               if (_inComponent)
+                       _texts.push_back(text);
+       }
+
+       void Render::AddHyperlink(GVJ_t* job, Hyperlink* hyperlink)
+       {
+               /* if in component, accumulate for end node/edge */
+               if (_inComponent)
+                       _hyperlinks.push_back(hyperlink);
+       }
+       
+       void Render::PrintOuterShape(GVJ_t* job, Graphic* graphic)
+       {
+               boxf bounds = graphic->GetBounds();
+               
+               gvprintf(job, "<Shape ID='%d' Type='Shape'>\n", ++_shapeId);
+               
+               gvputs(job, "<XForm>\n");
+               gvprintf(job, "<PinX>%f</PinX>\n", (bounds.LL.x + bounds.UR.x) * 0.5 * INCHES_PER_POINT);
+               gvprintf(job, "<PinY>%f</PinY>\n", (bounds.LL.y + bounds.UR.y) * 0.5 * INCHES_PER_POINT);
+               gvprintf(job, "<Width>%f</Width>\n", (bounds.UR.x - bounds.LL.x) * INCHES_PER_POINT);
+               gvprintf(job, "<Height>%f</Height>\n", (bounds.UR.y - bounds.LL.y) * INCHES_PER_POINT);
+               gvputs(job, "</XForm>\n");
+               
+               gvputs(job, "<Misc>\n");
+               gvputs(job, "<ObjType>1</ObjType>\n");
+               gvputs(job, "</Misc>\n");
+               
+               /* output Hyperlink */
+               PrintHyperlinks(job);
+
+               /* output Para, Char, Text */
+               PrintTexts(job);
+               
+               /* output Line, Fill, Geom */
+               graphic->Print(job, bounds.LL, bounds.UR);
+               
+               gvputs(job, "</Shape>\n");
+       }
+       
+       void Render::PrintInnerShape(GVJ_t* job, Graphic* graphic, unsigned int outerId, boxf outerBounds)
+       {
+               boxf innerBounds = graphic->GetBounds();
+               
+               /* compute scale. if infinite, scale by 0 instead */
+               double xscale = 1.0 / (outerBounds.UR.x - outerBounds.LL.x);
+               double yscale = 1.0 / (outerBounds.UR.y - outerBounds.LL.y);
+               if (isinf(xscale))
+                       xscale = 0.0;
+               if (isinf(yscale))
+                       yscale = 0.0;
+               
+               gvprintf(job, "<Shape ID='%d' Type='Shape'>\n", ++_shapeId);
+               
+               /* inner XForm is based on width or height of outer Shape */
+               gvputs(job, "<XForm>\n");
+               gvprintf(job, "<PinX F='Sheet.%d!Width*%f' />\n", outerId, (((innerBounds.LL.x + innerBounds.UR.x) * 0.5) - outerBounds.LL.x) * xscale);
+               gvprintf(job, "<PinY F='Sheet.%d!Height*%f' />\n", outerId, (((innerBounds.LL.y + innerBounds.UR.y) * 0.5) - outerBounds.LL.y) * yscale);
+               gvprintf(job, "<Width F='Sheet.%d!Width*%f' />\n", outerId, (innerBounds.UR.x - innerBounds.LL.x) * xscale);
+               gvprintf(job, "<Height F='Sheet.%d!Height*%f' />\n", outerId, (innerBounds.UR.y - innerBounds.LL.y) * yscale);
+               gvputs(job, "</XForm>\n");
+               
+               gvputs(job, "<Misc>\n");
+               gvputs(job, "<ObjType>1</ObjType>\n");
+               gvputs(job, "</Misc>\n");
+               
+               /* output Line, Fill, Geom */
+               graphic->Print(job, innerBounds.LL, innerBounds.UR);
+
+               gvputs(job, "</Shape>\n");
+       }
+       
+       void Render::PrintEdgeShape(GVJ_t* job, Graphic* graphic, unsigned int beginId, unsigned int endId)
+       {
+               pointf first = graphic->GetFirst();
+               pointf last = graphic->GetLast();
+               
+               bool zeroWidth = first.x == last.x;
+               bool zeroHeight = first.y == last.y;
+
+               gvprintf(job, "<Shape ID='%d' Type='Shape'>\n", ++_shapeId);
+               
+               /* XForm depends on XForm1D */
+               gvputs(job, "<XForm>\n");
+               gvputs(job, "<PinX F='GUARD((BeginX+EndX)/2)'/>\n");
+               gvputs(job, "<PinY F='GUARD((BeginY+EndY)/2)'/>\n");
+               if (zeroWidth)
+                       gvprintf(job, "<Width F='GUARD(%f)'/>\n", 2 * ZERO_ADJUST);     /* if vertical line, expand width to 0.25 inches */
+               else
+                       gvputs(job, "<Width F='GUARD(EndX-BeginX)'/>\n");
+               if (zeroHeight)
+                       gvprintf(job, "<Height F='GUARD(%f)'/>\n", 2 * ZERO_ADJUST);    /* if horizontal line, expand height to 0.25 inches */
+               else
+                       gvputs(job, "<Height F='GUARD(EndY-BeginY)'/>\n");
+               gvputs(job, "<Angle F='GUARD(0DA)'/>\n");
+               gvputs(job, "</XForm>\n");
+                       
+               /* XForm1D walking glue makes connector attach to its nodes */
+               gvputs(job, "<XForm1D>\n");
+               gvprintf(job, "<BeginX F='_WALKGLUE(BegTrigger,EndTrigger,WalkPreference)'>%f</BeginX>\n", first.x * INCHES_PER_POINT);
+               gvprintf(job, "<BeginY F='_WALKGLUE(BegTrigger,EndTrigger,WalkPreference)'>%f</BeginY>\n", first.y * INCHES_PER_POINT);
+               gvprintf(job, "<EndX F='_WALKGLUE(EndTrigger,BegTrigger,WalkPreference)'>%f</EndX>\n", last.x * INCHES_PER_POINT);
+               gvprintf(job, "<EndY F='_WALKGLUE(EndTrigger,BegTrigger,WalkPreference)'>%f</EndY>\n", last.y * INCHES_PER_POINT);
+               gvputs(job, "</XForm1D>\n");
+               
+               gvputs(job, "<Protection>\n");
+               gvputs(job, "<LockHeight>1</LockHeight>\n");
+               gvputs(job, "<LockCalcWH>1</LockCalcWH>\n");
+               gvputs(job, "</Protection>\n");
+               
+               gvputs(job, "<Misc>\n");
+               gvputs(job, "<NoAlignBox>1</NoAlignBox>\n");
+               gvputs(job, "<DynFeedback>2</DynFeedback>\n");
+               gvputs(job, "<GlueType>2</GlueType>\n");
+               if (beginId && endId)
+               {
+                       gvprintf(job, "<BegTrigger F='_XFTRIGGER(Sheet.%d!EventXFMod)'/>\n", beginId);
+                       gvprintf(job, "<EndTrigger F='_XFTRIGGER(Sheet.%d!EventXFMod)'/>\n", endId);
+               }
+               gvputs(job, "<ObjType>2</ObjType>\n");
+               gvputs(job, "</Misc>\n");
+               
+               gvputs(job, "<Layout>\n");
+               gvputs(job, "<ShapeRouteStyle>1</ShapeRouteStyle>\n");
+               gvputs(job, "<ConFixedCode>6</ConFixedCode>\n");
+               gvputs(job, "<ConLineRouteExt>2</ConLineRouteExt>\n");
+               gvputs(job, "<ShapeSplittable>1</ShapeSplittable>\n");
+               gvputs(job, "</Layout>\n");
+               
+               /* output Hyperlink */
+               PrintHyperlinks(job);
+
+               /* TextXForm depends on custom control */
+               gvputs(job, "<TextXForm>\n");
+               gvputs(job, "<TxtPinX F='SETATREF(Controls.TextPosition)'/>\n");
+               gvputs(job, "<TxtPinY F='SETATREF(Controls.TextPosition.Y)'/>\n");
+               gvputs(job, "<TxtWidth F='MAX(TEXTWIDTH(TheText),5*Char.Size)'/>\n");
+               gvputs(job, "<TxtHeight F='TEXTHEIGHT(TheText,TxtWidth)'/>\n");
+               gvputs(job, "</TextXForm>\n");
+
+               if (zeroWidth)
+               {
+                       first.x -= ZERO_ADJUST;
+                       last.x += ZERO_ADJUST;
+               }
+               if (zeroHeight)
+               {
+                       first.y -= ZERO_ADJUST;
+                       last.y += ZERO_ADJUST;
+               }
+               
+               /* compute center to attach text to. if text has been rendered, use overall bounding box center; if not, use the path center */
+               pointf textCenter;
+               if (_texts.size() > 0)
+               {
+                       boxf outerTextBounds = _texts[0]->GetBounds();
+                       
+                       for (Texts::const_iterator nextText = _texts.begin() + 1, lastText = _texts.end();
+                                nextText != lastText;
+                                ++nextText)
+                       {
+                               boxf innerTextBounds = (*nextText)->GetBounds();
+                               if (outerTextBounds.LL.x > innerTextBounds.LL.x)
+                                       outerTextBounds.LL.x = innerTextBounds.LL.x;
+                               if (outerTextBounds.LL.y > innerTextBounds.LL.y)
+                                       outerTextBounds.LL.y = innerTextBounds.LL.y;
+                               if (outerTextBounds.UR.x < innerTextBounds.UR.x)
+                                       outerTextBounds.UR.x = innerTextBounds.UR.x;
+                               if (outerTextBounds.UR.y < innerTextBounds.UR.y)
+                                       outerTextBounds.UR.y = innerTextBounds.UR.y;
+                       }
+                       textCenter.x = (outerTextBounds.LL.x + outerTextBounds.UR.x) / 2.0;
+                       textCenter.y = (outerTextBounds.LL.y + outerTextBounds.UR.y) / 2.0;
+               }
+               else
+                       textCenter = graphic->GetCenter();
+               
+               /* Control for positioning text */
+               gvputs(job, "<Control NameU='TextPosition'>\n");
+               gvprintf(job, "<X>%f</X>\n", (textCenter.x - first.x) * INCHES_PER_POINT);
+               gvprintf(job, "<Y>%f</Y>\n", (textCenter.y - first.y) * INCHES_PER_POINT);
+               gvputs(job, "<XDyn F='Controls.TextPosition'/>\n");
+               gvputs(job, "<YDyn F='Controls.TextPosition.Y'/>\n");
+               gvputs(job, "<XCon F='IF(OR(STRSAME(SHAPETEXT(TheText),&quot;&quot;),HideText),5,0)'>5</XCon>\n");
+               gvputs(job, "<YCon>0</YCon>\n");
+               gvputs(job, "</Control>\n");
+                       
+               /* output Para, Char, Text */
+               PrintTexts(job);
+               
+               /* output Line, Fill, Geom */
+               graphic->Print(job, first, last);
+               
+               gvputs(job, "</Shape>\n");
+       }
+       
+       void Render::PrintTexts(GVJ_t* job)
+       {
+               if (_texts.size() > 0)
+               {
+                       /* output Para, Char */
+                       for (Texts::iterator nextText = _texts.begin(), lastText = _texts.end(); nextText != lastText; ++nextText)
+                               (*nextText)->Print(job);
+                       
+                       /* output Text. each run references above Para + Char */
+                       gvputs(job, "<Text>");
+                       for (unsigned int index = 0, count = _texts.size(); index < count; ++index)
+                               (_texts[index])->PrintRun(job, index);
+                       gvputs(job, "</Text>");
+               }
+       }
+       
+       void Render::PrintHyperlinks(GVJ_t* job)
+       {
+               if (_hyperlinks.size() > 0)
+               {
+                       _hyperlinks[0]->Print(job, ++_hyperlinkId, true);
+                       for (Hyperlinks::iterator nextHyperlink = _hyperlinks.begin() + 1, lastHyperlink = _hyperlinks.end(); nextHyperlink != lastHyperlink; ++nextHyperlink)
+                               (*nextHyperlink)->Print(job, ++_hyperlinkId, false);
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/plugin/visio/VisioRender.h b/plugin/visio/VisioRender.h
new file mode 100644 (file)
index 0000000..f3f01aa
--- /dev/null
@@ -0,0 +1,95 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+ *      This software is part of the graphviz package      *
+ *                http://www.graphviz.org/                 *
+ *                                                         *
+ *            Copyright (c) 1994-2004 AT&T Corp.           *
+ *                and is licensed under the                *
+ *            Common Public License, Version 1.0           *
+ *                      by AT&T Corp.                      *
+ *                                                         *
+ *        Information and Software Systems Research        *
+ *              AT&T Research, Florham Park NJ             *
+ **********************************************************/
+
+#ifndef VISIORENDER_H
+#define VISIORENDER_H
+
+#include <map>
+#include <vector>
+
+#include "types.h"
+
+#include "VisioGraphic.h"
+#include "VisioText.h"
+
+namespace Visio
+{
+       typedef std::map<Agnode_t*, unsigned int> NodeIds;
+       typedef std::vector<Graphic*> Graphics;
+       typedef std::vector<Text*> Texts;
+       typedef std::vector<Hyperlink*> Hyperlinks;
+
+       /* object wrapper for render function callback */
+       class Render
+       {
+       public:
+               Render();
+               ~Render();
+               
+               /* render hierarchy */
+               void BeginGraph(GVJ_t* job);
+               void EndGraph(GVJ_t* job);
+               void BeginPage(GVJ_t* job);
+               void EndPage(GVJ_t* job);
+               void BeginNode(GVJ_t* job);
+               void EndNode(GVJ_t* job);
+               void BeginEdge(GVJ_t* job);
+               void EndEdge(GVJ_t* job);
+               
+               /* render graphic + text */
+               void AddEllipse(GVJ_t* job, pointf* A, bool filled);
+               void AddBezier(GVJ_t* job, pointf* A, int n, bool arrow_at_start, bool arrow_at_end, bool filled);
+               void AddPolygon(GVJ_t* job, pointf* A, int n, bool filled);
+               void AddPolyline(GVJ_t* job, pointf* A, int n);
+               void AddText(GVJ_t *job, pointf p, textpara_t *para);
+               void AddAnchor(GVJ_t *job, char *url, char *tooltip, char *target, char *id);
+               
+       private:
+               /* graphics and texts maintenance */
+               void ClearGraphicsAndTexts();
+               void AddGraphic(GVJ_t* job, Graphic* graphic);
+               void AddText(GVJ_t* job, Text* text);
+               void AddHyperlink(GVJ_t* job, Hyperlink* hyperlink);
+               
+               /* output the graphic as top level shape */
+               void PrintOuterShape(GVJ_t* job, Graphic* graphic);
+               
+               /* output the graphic as a subshape of a top level shape, given its id and bounds */
+               void PrintInnerShape(GVJ_t* job, Graphic* graphic, unsigned int outerId, boxf outerBounds);
+               
+               /* output the graphic as an edge shape, given the start and end node ids */
+               void PrintEdgeShape(GVJ_t* job, Graphic* graphic, unsigned int beginId, unsigned int endId);
+               
+               /* output all the collected texts */
+               void PrintTexts(GVJ_t* job);
+
+               /* output all the collected hyperlinks */
+               void PrintHyperlinks(GVJ_t* job);
+
+               unsigned int _pageId;   /* sequential page id, starting from 1 */
+               unsigned int _shapeId;  /* sequential shape id, starting from 1 */
+               unsigned int _hyperlinkId;      /* sequential shape id, starting from 1 */
+               
+               bool _inComponent;              /* whether we currently inside a node/edge, or not */
+               
+               Graphics _graphics;             /* currently collected graphics within a component */
+               Texts _texts;                   /* currently collected texts within a component */
+               Hyperlinks _hyperlinks; /* currently collected hyperlinks within a component */
+               
+               NodeIds _nodeIds;               /* mapping nodes to assigned shape id */
+       };
+}
+#endif
\ No newline at end of file
diff --git a/plugin/visio/VisioText.cpp b/plugin/visio/VisioText.cpp
new file mode 100644 (file)
index 0000000..6d5e6c0
--- /dev/null
@@ -0,0 +1,188 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/**********************************************************
+ *      This software is part of the graphviz package      *
+ *                http://www.graphviz.org/                 *
+ *                                                         *
+ *            Copyright (c) 1994-2004 AT&T Corp.           *
+ *                and is licensed under the                *
+ *            Common Public License, Version 1.0           *
+ *                      by AT&T Corp.                      *
+ *                                                         *
+ *        Information and Software Systems Research        *
+ *              AT&T Research, Florham Park NJ             *
+ **********************************************************/
+
+#include "VisioText.h"
+
+#include "gvcjob.h"
+#include "gvio.h"
+
+namespace Visio
+{
+       static const float INCHES_PER_POINT = 1.0 / 72.0;
+
+       Char::Char(double size, unsigned char red, unsigned char green, unsigned char blue):
+               _size(size),
+               _red(red),
+               _green(green),
+               _blue(blue)
+       {
+       }
+       
+       void Char::Print(GVJ_t* job) const
+       {
+               gvputs(job, "<Char>\n");
+               gvprintf(job, "<Color>#%02X%02X%02X</Color>\n", _red, _green, _blue);
+               gvprintf(job, "<Size>%f</Size>\n", _size * job->scale.x * INCHES_PER_POINT);    /* scale font size, VDX uses inches */
+               gvputs(job, "</Char>\n");
+       }
+       
+       Para::Para(HorzAlign horzAlign):
+               _horzAlign(horzAlign)
+       {
+       }
+       
+       void Para::Print(GVJ_t* job) const
+       {
+               gvputs(job, "<Para>\n");
+               gvprintf(job, "<HorzAlign>%d</HorzAlign>\n", _horzAlign);
+               gvputs(job, "</Para>\n");
+       }
+       
+       Run::Run(boxf bounds, char* text):
+               _bounds(bounds),
+               _text(agstrdup(text))   /* copy text */
+       {
+       }
+       
+       Run::~Run()
+       {
+               /* since we copied, we need to free */
+               agstrfree(_text);
+       }
+       
+       boxf Run::GetBounds() const
+       {
+               return _bounds;
+       }
+       
+       void Run::Print(GVJ_t* job, unsigned int index) const
+       {
+               gvprintf(job, "<pp IX='%d'/><cp IX='%d'/>%s\n", index, index, _text ? _text : "");      /* para mark + char mark + actual text */
+       }
+       
+       Text* Text::CreateText(GVJ_t* job, pointf p, textpara_t* para)
+       {
+               Para::HorzAlign horzAlign;
+               
+               /* compute text bounding box and VDX horizontal align */
+               boxf bounds;
+               bounds.LL.y = p.y + para->yoffset_centerline;
+               bounds.UR.y = p.y + para->yoffset_centerline + para->height;
+               double width = para->width;
+               switch (para->just)
+               {
+                       case 'r':
+                               horzAlign = Para::horzRight;
+                               bounds.LL.x = p.x - width;
+                               bounds.UR.x = p.x;
+                               break;
+                       case 'l':
+                               horzAlign = Para::horzLeft;
+                               bounds.LL.x = p.x;
+                               bounds.UR.x = p.x + width;
+                               break;
+                       case 'n':
+                       default:
+                               horzAlign = Para::horzCenter;
+                               bounds.LL.x = p.x - width / 2.0;
+                               bounds.UR.x = p.x + width / 2.0;
+                               break;
+               }
+               
+               return new Text(
+                       new Para(
+                               horzAlign),
+                       new Char(
+                               para->fontsize,
+                               job->obj->pencolor.u.rgba[0],
+                               job->obj->pencolor.u.rgba[1],
+                               job->obj->pencolor.u.rgba[2]),
+                       new Run(
+                               bounds,
+                               para->str));
+       }
+       
+       Text::Text(Para* para, Char* chars, Run* run):
+               _para(para),
+               _chars(chars),
+               _run(run)
+       {
+       }
+       
+       Text::~Text()
+       {
+               if (_para)
+                       delete _para;
+               if (_chars)
+                       delete _chars;
+               if (_run)
+                       delete _run;
+       }
+       
+       boxf Text::GetBounds() const
+       {
+               return _run->GetBounds();
+       }
+       
+       void Text::Print(GVJ_t* job) const
+       {
+               if (_para)
+                       _para->Print(job);
+               if (_chars)
+                       _chars->Print(job);
+       }
+       
+       void Text::PrintRun(GVJ_t* job, unsigned int index) const
+       {
+               if (_run)
+                       _run->Print(job, index);
+       }
+
+       Hyperlink* Hyperlink::CreateHyperlink(GVJ_t* job, char* url, char* tooltip, char* target, char* id)
+       {
+               return new Hyperlink(tooltip, url, target);
+       }
+
+       Hyperlink::Hyperlink(char* description, char* address, char* frame):
+               _description(agstrdup(description)),
+               _address(agstrdup(address)),
+               _frame(agstrdup(frame))
+       {
+       }
+       
+       Hyperlink::~Hyperlink()
+       {
+               agstrfree(_description);
+               agstrfree(_address);
+               agstrfree(_frame);
+       }
+       
+       /* output the hyperlink */
+       void Hyperlink::Print(GVJ_t* job, unsigned int id, bool isDefault) const
+       {
+               gvprintf(job, "<Hyperlink ID='%d'>\n", id);
+               if (_description)
+                       gvprintf(job, "<Description>%s</Description>\n", _description);
+               if (_address)
+                       gvprintf(job, "<Address>%s</Address>\n", _address);
+               if (_frame)
+                       gvprintf(job, "<Frame>%s</Frame>\n", _frame);
+               if (isDefault)
+                       gvputs(job, "<Default>1</Default>\n");
+               gvputs(job, "</Hyperlink>\n");
+       }
+       
+}
\ No newline at end of file