--- /dev/null
+/* $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
--- /dev/null
+/* $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
--- /dev/null
+/* $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),""),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
--- /dev/null
+/* $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
--- /dev/null
+/* $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