From 58f77ad00cd121c893bfd4a3c2ca81beec38edf5 Mon Sep 17 00:00:00 2001 From: glenlow Date: Tue, 31 Mar 2009 13:14:48 +0000 Subject: [PATCH] Visio VDX render plugin --- plugin/visio/VisioGraphic.cpp | 552 ++++++++++++++++++++++++++++++++++ plugin/visio/VisioGraphic.h | 170 +++++++++++ plugin/visio/VisioRender.cpp | 453 ++++++++++++++++++++++++++++ plugin/visio/VisioRender.h | 95 ++++++ plugin/visio/VisioText.cpp | 188 ++++++++++++ 5 files changed, 1458 insertions(+) create mode 100644 plugin/visio/VisioGraphic.cpp create mode 100644 plugin/visio/VisioGraphic.h create mode 100644 plugin/visio/VisioRender.cpp create mode 100644 plugin/visio/VisioRender.h create mode 100644 plugin/visio/VisioText.cpp diff --git a/plugin/visio/VisioGraphic.cpp b/plugin/visio/VisioGraphic.cpp new file mode 100644 index 000000000..ff3a193aa --- /dev/null +++ b/plugin/visio/VisioGraphic.cpp @@ -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 + +#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, "\n"); + gvprintf(job, "#%02X%02X%02X\n", _red, _green, _blue); /* VDX uses hex colors */ + gvprintf(job, "%f\n", _transparency); + gvputs(job, "\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, "\n"); + gvprintf(job, "%f\n", _weight * job->scale.x * INCHES_PER_POINT); /* scale line weight, VDX uses inches */ + gvprintf(job, "#%02X%02X%02X\n", _red, _green, _blue); /* VDX uses hex colors */ + if (_pattern) + gvprintf(job, "%d\n", _pattern); + if (_beginArrow) + gvprintf(job, "%d\n", _beginArrow); + if (_endArrow) + gvprintf(job, "%d\n", _endArrow); + gvputs(job, "\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, "\n"); + if (!_filled) + gvputs(job, "1\n"); /* omit fill? */ + gvputs(job, "\n"); + gvputs(job, "0\n"); /* semi ellipse */ + gvputs(job, "0\n"); /* semi ellipse */ + gvputs(job, "\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, "\n"); + if (!_filled) + gvputs(job, "1\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, ""); + gvprintf(job, "", (_points[0].x - first.x) * xscale); + gvprintf(job, "", (_points[0].y - first.y) * yscale); + gvputs(job, "\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, ""); + + /* Ctl[P-1].X */ + gvprintf(job, "", (_points[_pointCount - 1].x - first.x) * xscale); + + /* Ctl[P-1].Y */ + gvprintf(job, "", (_points[_pointCount - 1].y - first.y) * yscale); + + /* Knot[P-1] */ + gvprintf(job, "%d", std::max(_pointCount - 4, 0)); + + /* Ctl[P-1].Weight */ + gvputs(job, "1"); + + /* Knot[0] */ + gvputs(job, "0"); + + /* Weight[0] */ + gvputs(job, "1"); + + /* Knot[P], Degree, XType, YType */ + gvprintf(job, ""); + + gvputs(job, "\n"); + } + gvputs(job, "\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, "\n"); + if (!_filled) + gvputs(job, "1\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, ""); + gvprintf(job, "", (_points[0].x - first.x) * xscale); + gvprintf(job, "", (_points[0].y - first.y) * yscale); + gvputs(job, "\n"); + + if (_pointCount == 1) + { + /* single point, use VDX LineTo */ + gvputs(job, ""); + gvprintf(job, "", (_points[0].x - first.x) * xscale); + gvprintf(job, "", (_points[0].y - first.y) * yscale); + gvputs(job, "\n"); + } + else + { + /* multiple points, use VDX PolylineTo */ + gvputs(job, ""); + gvprintf(job, "", (_points[0].x - first.x) * xscale); + gvprintf(job, "", (_points[0].y - first.y) * yscale); + gvputs(job, ""); + gvputs(job, "\n"); + } + } + gvputs(job, "\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, "\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, ""); + gvprintf(job, "", (_points[0].x - first.x) * xscale); + gvprintf(job, "", (_points[0].y - first.y) * yscale); + gvputs(job, "\n"); + + if (_pointCount == 2) + { + /* single point, use VDX LineTo */ + gvputs(job, ""); + gvprintf(job, "", (_points[1].x - first.x) * xscale); + gvprintf(job, "", (_points[1].y - first.y) * yscale); + gvputs(job, "\n"); + } + else + { + /* multiple points, use VDX PolylineTo */ + gvputs(job, ""); + gvprintf(job, "", (_points[_pointCount - 1].x - first.x) * xscale); + gvprintf(job, "", (_points[_pointCount - 1].y - first.y) * yscale); + gvputs(job, ""); + gvputs(job, "\n"); + } + } + gvputs(job, "\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 index 000000000..d8512c6e7 --- /dev/null +++ b/plugin/visio/VisioGraphic.h @@ -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 index 000000000..8a0874c37 --- /dev/null +++ b/plugin/visio/VisioRender.cpp @@ -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, "\n"); + gvputs(job, "\n"); + } + + void Render::EndGraph(GVJ_t* job) + { + gvputs(job, "\n"); + gvputs(job, "\n"); + } + + void Render::BeginPage(GVJ_t* job) + { + gvprintf(job, "\n", ++_pageId); + + gvputs(job, "\n"); + gvputs(job, "\n"); + gvprintf(job, "%f\n", job->width * INCHES_PER_POINT); + gvprintf(job, "%f\n", job->height * INCHES_PER_POINT); + gvputs(job, "\n"); + gvputs(job, "\n"); + + gvputs(job, ""); + } + + void Render::EndPage(GVJ_t* job) + { + gvputs(job, "\n"); + gvputs(job, "\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, "\n", ++_shapeId); + outerShapeId = _shapeId; + + gvputs(job, "\n"); + gvprintf(job, "%f\n", (outerBounds.LL.x + outerBounds.UR.x) * 0.5 * INCHES_PER_POINT); + gvprintf(job, "%f\n", (outerBounds.LL.y + outerBounds.UR.y) * 0.5 * INCHES_PER_POINT); + gvprintf(job, "%f\n", (outerBounds.UR.x - outerBounds.LL.x) * INCHES_PER_POINT); + gvprintf(job, "%f\n", (outerBounds.UR.y - outerBounds.LL.y) * INCHES_PER_POINT); + gvputs(job, "\n"); + + /* output Hyperlink */ + PrintHyperlinks(job); + + /* output Para, Char */ + PrintTexts(job); + + /* output subshapes */ + gvputs(job, "\n"); + for (Graphics::const_iterator nextGraphic = _graphics.begin(), lastGraphic = _graphics.end(); nextGraphic != lastGraphic; ++nextGraphic) + PrintInnerShape(job, *nextGraphic, outerShapeId, outerBounds); + gvputs(job, "\n"); + + gvputs(job, "\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, "\n", ++_shapeId); + + gvputs(job, "\n"); + gvprintf(job, "%f\n", (bounds.LL.x + bounds.UR.x) * 0.5 * INCHES_PER_POINT); + gvprintf(job, "%f\n", (bounds.LL.y + bounds.UR.y) * 0.5 * INCHES_PER_POINT); + gvprintf(job, "%f\n", (bounds.UR.x - bounds.LL.x) * INCHES_PER_POINT); + gvprintf(job, "%f\n", (bounds.UR.y - bounds.LL.y) * INCHES_PER_POINT); + gvputs(job, "\n"); + + gvputs(job, "\n"); + gvputs(job, "1\n"); + gvputs(job, "\n"); + + /* output Hyperlink */ + PrintHyperlinks(job); + + /* output Para, Char, Text */ + PrintTexts(job); + + /* output Line, Fill, Geom */ + graphic->Print(job, bounds.LL, bounds.UR); + + gvputs(job, "\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, "\n", ++_shapeId); + + /* inner XForm is based on width or height of outer Shape */ + gvputs(job, "\n"); + gvprintf(job, "\n", outerId, (((innerBounds.LL.x + innerBounds.UR.x) * 0.5) - outerBounds.LL.x) * xscale); + gvprintf(job, "\n", outerId, (((innerBounds.LL.y + innerBounds.UR.y) * 0.5) - outerBounds.LL.y) * yscale); + gvprintf(job, "\n", outerId, (innerBounds.UR.x - innerBounds.LL.x) * xscale); + gvprintf(job, "\n", outerId, (innerBounds.UR.y - innerBounds.LL.y) * yscale); + gvputs(job, "\n"); + + gvputs(job, "\n"); + gvputs(job, "1\n"); + gvputs(job, "\n"); + + /* output Line, Fill, Geom */ + graphic->Print(job, innerBounds.LL, innerBounds.UR); + + gvputs(job, "\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, "\n", ++_shapeId); + + /* XForm depends on XForm1D */ + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "\n"); + if (zeroWidth) + gvprintf(job, "\n", 2 * ZERO_ADJUST); /* if vertical line, expand width to 0.25 inches */ + else + gvputs(job, "\n"); + if (zeroHeight) + gvprintf(job, "\n", 2 * ZERO_ADJUST); /* if horizontal line, expand height to 0.25 inches */ + else + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "\n"); + + /* XForm1D walking glue makes connector attach to its nodes */ + gvputs(job, "\n"); + gvprintf(job, "%f\n", first.x * INCHES_PER_POINT); + gvprintf(job, "%f\n", first.y * INCHES_PER_POINT); + gvprintf(job, "%f\n", last.x * INCHES_PER_POINT); + gvprintf(job, "%f\n", last.y * INCHES_PER_POINT); + gvputs(job, "\n"); + + gvputs(job, "\n"); + gvputs(job, "1\n"); + gvputs(job, "1\n"); + gvputs(job, "\n"); + + gvputs(job, "\n"); + gvputs(job, "1\n"); + gvputs(job, "2\n"); + gvputs(job, "2\n"); + if (beginId && endId) + { + gvprintf(job, "\n", beginId); + gvprintf(job, "\n", endId); + } + gvputs(job, "2\n"); + gvputs(job, "\n"); + + gvputs(job, "\n"); + gvputs(job, "1\n"); + gvputs(job, "6\n"); + gvputs(job, "2\n"); + gvputs(job, "1\n"); + gvputs(job, "\n"); + + /* output Hyperlink */ + PrintHyperlinks(job); + + /* TextXForm depends on custom control */ + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "\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, "\n"); + gvprintf(job, "%f\n", (textCenter.x - first.x) * INCHES_PER_POINT); + gvprintf(job, "%f\n", (textCenter.y - first.y) * INCHES_PER_POINT); + gvputs(job, "\n"); + gvputs(job, "\n"); + gvputs(job, "5\n"); + gvputs(job, "0\n"); + gvputs(job, "\n"); + + /* output Para, Char, Text */ + PrintTexts(job); + + /* output Line, Fill, Geom */ + graphic->Print(job, first, last); + + gvputs(job, "\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, ""); + for (unsigned int index = 0, count = _texts.size(); index < count; ++index) + (_texts[index])->PrintRun(job, index); + gvputs(job, ""); + } + } + + 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 index 000000000..f3f01aa26 --- /dev/null +++ b/plugin/visio/VisioRender.h @@ -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 +#include + +#include "types.h" + +#include "VisioGraphic.h" +#include "VisioText.h" + +namespace Visio +{ + typedef std::map NodeIds; + typedef std::vector Graphics; + typedef std::vector Texts; + typedef std::vector 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 index 000000000..6d5e6c044 --- /dev/null +++ b/plugin/visio/VisioText.cpp @@ -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, "\n"); + gvprintf(job, "#%02X%02X%02X\n", _red, _green, _blue); + gvprintf(job, "%f\n", _size * job->scale.x * INCHES_PER_POINT); /* scale font size, VDX uses inches */ + gvputs(job, "\n"); + } + + Para::Para(HorzAlign horzAlign): + _horzAlign(horzAlign) + { + } + + void Para::Print(GVJ_t* job) const + { + gvputs(job, "\n"); + gvprintf(job, "%d\n", _horzAlign); + gvputs(job, "\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, "%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, "\n", id); + if (_description) + gvprintf(job, "%s\n", _description); + if (_address) + gvprintf(job, "
%s
\n", _address); + if (_frame) + gvprintf(job, "%s\n", _frame); + if (isDefault) + gvputs(job, "1\n"); + gvputs(job, "
\n"); + } + +} \ No newline at end of file -- 2.40.0