#include "render.h"
#include "agxbuf.h"
+#define EPSILON .0001
+
static char *defaultlinestyle[3] = { "solid\0", "setlinewidth\0001\0", 0 };
int emitState;
case GVRENDER_PLUGIN:
job->flags = chkOrder(g) | job->render.features->flags;
break;
- case POSTSCRIPT:
- job->flags = chkOrder(g) | GVRENDER_DOES_TRANSFORM | GVRENDER_DOES_MULTIGRAPH_OUTPUT_FILES;
- break;
- case SVG:
- job->flags = chkOrder(g) | GVRENDER_Y_GOES_DOWN;
- break;
- case GD: /* bitmap formats supported by gdgen.c */
- case GD2:
- case GIF:
- case JPEG:
- case PNG:
- case WBMP:
- case XBM:
- job->flags = chkOrder(g) | GVRENDER_Y_GOES_DOWN;
- break;
- case ISMAP: case IMAP: case CMAP: case CMAPX:
- /* output in breadth first graph walk order, but
- * with nodes edges and nested clusters before
- * clusters */
- job->flags = EMIT_CLUSTERS_LAST;
- break;
- case FIG:
- /* output color definition objects first */
- job->flags = EMIT_COLORS;
- break;
case VTX:
/* output sorted, i.e. all nodes then all edges */
job->flags = EMIT_SORTED;
static void init_job_pagination(GVJ_t * job, graph_t *g)
{
GVC_t *gvc = job->gvc;
- point pageSize; /* page size for the graph - device units */
- point imageSize; /* image size on one page of the graph - device units */
- point margin; /* margin for a page of the graph - device units */
+ pointf pageSize; /* page size for the graph - points*/
+ pointf imageSize; /* image size on one page of the graph - points */
+ pointf margin; /* margin for a page of the graph - points */
/* unpaginated image size in device units */
- imageSize.x = job->width;
- imageSize.y = job->height;
- if (GD_drawing(g)->landscape)
- imageSize = exch_xy(imageSize);
+ imageSize = job->view;
+ if (job->rotation)
+ imageSize = exch_xyf(imageSize);
/* determine pagination */
if (gvc->graph_sets_pageSize) {
/* page was set by user */
- pageSize.x = ROUND(gvc->pageSize.x * job->dpi.x / POINTS_PER_INCH);
- pageSize.y = ROUND(gvc->pageSize.y * job->dpi.y / POINTS_PER_INCH);
+ pageSize = gvc->pageSize;
/* we don't want graph page to exceed its bounding box */
pageSize.x = MIN(pageSize.x, imageSize.x);
pageSize.y = MIN(pageSize.y, imageSize.y);
- if (pageSize.x == 0)
+ if (pageSize.x < EPSILON)
job->pagesArraySize.x = 1;
else {
- job->pagesArraySize.x = imageSize.x / pageSize.x;
- if (imageSize.x % pageSize.x)
+ job->pagesArraySize.x = (int)(imageSize.x / pageSize.x);
+ if ((imageSize.x - (job->pagesArraySize.x * pageSize.x)) > EPSILON)
job->pagesArraySize.x++;
}
- if (pageSize.y == 0)
+ if (pageSize.y < EPSILON)
job->pagesArraySize.y = 1;
else {
- job->pagesArraySize.y = imageSize.y / pageSize.y;
- if (imageSize.y % pageSize.y)
+ job->pagesArraySize.y = (int)(imageSize.y / pageSize.y);
+ if ((imageSize.y - (job->pagesArraySize.y * pageSize.y)) > EPSILON)
job->pagesArraySize.y++;
}
job->numPages = job->pagesArraySize.x * job->pagesArraySize.y;
}
/* size of one page in graph units */
- job->pageSize.x = (double)imageSize.x * POINTS_PER_INCH / (job->dpi.x * job->zoom);
- job->pageSize.y = (double)imageSize.y * POINTS_PER_INCH / (job->dpi.y * job->zoom);
+ job->pageSize.x = imageSize.x / job->zoom;
+ job->pageSize.y = imageSize.y / job->zoom;
- margin.x = ROUND(job->margin.x * job->dpi.x / POINTS_PER_INCH);
- margin.y = ROUND(job->margin.y * job->dpi.y / POINTS_PER_INCH);
+ PF2P(job->margin, margin);
/* determine page box including centering */
if (GD_drawing(g)->centered) {
margin.y += (pageSize.y - imageSize.y) / 2;
}
- job->canvasBox.LL.x = ROUND(job->margin.x);
- job->canvasBox.LL.y = ROUND(job->margin.y);
- job->canvasBox.UR.x = ROUND(job->margin.x) + imageSize.x;
- job->canvasBox.UR.y = ROUND(job->margin.y) + imageSize.y;
-
- /* calculate job->width and job->height with margins */
- if (job->rotation) {
- job->width = job->canvasBox.UR.y + job->canvasBox.LL.y;
- job->height = job->canvasBox.UR.x + job->canvasBox.LL.x;
- }
- else {
- job->width = job->canvasBox.UR.x + job->canvasBox.LL.x;
- job->height = job->canvasBox.UR.y + job->canvasBox.LL.y;
- }
-
-
-#if 0
-fprintf(stderr,"margin = %d,%d imageSize = %d,%d pageBoundingBox = %d,%d %d,%d\n",
- margin.x, margin.y,
- imageSize.x, imageSize.x,
- job->pageBoundingBox.LL.x, job->pageBoundingBox.LL.y,
- job->pageBoundingBox.UR.x, job->pageBoundingBox.UR.x);
-#endif
+ job->canvasBox.LL.x = margin.x;
+ job->canvasBox.LL.y = margin.y;
+ job->canvasBox.UR.x = margin.x + imageSize.x;
+ job->canvasBox.UR.y = margin.y + imageSize.y;
/* set up pagedir */
job->pagesArrayMajor.x = job->pagesArrayMajor.y
job->pagesArrayMinor = pagecode(job, 'L');
agerr(AGWARN, "pagedir=%s ignored\n", gvc->pagedir);
}
-
-#if 0
-fprintf(stderr,"margin = %d,%d imageSize = %d,%d pageSize = %d,%d (device units)\n",
- margin.x, margin.y,
- imageSize.x, imageSize.y,
- pageSize.x, pageSize.y);
-fprintf(stderr,"job->pageSize= %g,%g (graph units)\n",
- job->pageSize.x, job->pageSize.y);
-fprintf(stderr,"dpi = %g,%g zoom = %g rotation = %d\n",
- job->dpi.x, job->dpi.y, job->zoom, job->rotation);
-fprintf(stderr,"pageBoundingBox = %d,%d %d,%d (device units)\n",
- job->pageBoundingBox.LL.x,
- job->pageBoundingBox.LL.y,
- job->pageBoundingBox.UR.x,
- job->pageBoundingBox.UR.y);
-fprintf(stderr,"width,height = %d,%d (device units)\n",
- job->width,
- job->height);
-fprintf (stderr,"pagedir = %s, pagesArrayMajor = %d,%d pagesArrayMinor = %d,%d\n",
- gvc->pagedir,
- job->pagesArrayMajor.x,
- job->pagesArrayMajor.y,
- job->pagesArrayMinor.x,
- job->pagesArrayMinor.y);
-fprintf (stderr,"pagesArrayFirst = %d,%d pagesArraySize = %d,%d numPages = %d\n",
- job->pagesArrayFirst.x,
- job->pagesArrayFirst.y,
- job->pagesArraySize.x,
- job->pagesArraySize.y,
- job->numPages);
-#endif
}
static void firstpage(GVJ_t *job)
job->pageBoxClip.LL.y = MAX(job->clip.LL.y, job->pageBox.LL.y);
if (job->rotation) {
- job->pageBoundingBox.LL.x = job->canvasBox.LL.y;
- job->pageBoundingBox.LL.y = job->canvasBox.LL.x;
- job->pageBoundingBox.UR.x = job->canvasBox.UR.y;
- job->pageBoundingBox.UR.y = job->canvasBox.UR.x;
+ job->pageBoundingBox.LL.x = ROUND(job->canvasBox.LL.y * job->dpi.x / POINTS_PER_INCH);
+ job->pageBoundingBox.LL.y = ROUND(job->canvasBox.LL.x * job->dpi.y / POINTS_PER_INCH);
+ job->pageBoundingBox.UR.x = ROUND(job->canvasBox.UR.y * job->dpi.x / POINTS_PER_INCH);
+ job->pageBoundingBox.UR.y = ROUND(job->canvasBox.UR.x * job->dpi.y / POINTS_PER_INCH);
}
else {
- job->pageBoundingBox = job->canvasBox;
+ job->pageBoundingBox.LL.x = ROUND(job->canvasBox.LL.x * job->dpi.x / POINTS_PER_INCH);
+ job->pageBoundingBox.LL.y = ROUND(job->canvasBox.LL.y * job->dpi.y / POINTS_PER_INCH);
+ job->pageBoundingBox.UR.x = ROUND(job->canvasBox.UR.x * job->dpi.x / POINTS_PER_INCH);
+ job->pageBoundingBox.UR.y = ROUND(job->canvasBox.UR.y * job->dpi.y / POINTS_PER_INCH);
}
if (job->common->viewNum == 0)
if (job->rotation) {
if (job->flags & GVRENDER_Y_GOES_DOWN) {
- job->translation.x = -job->pageBox.UR.x - job->pageBoundingBox.LL.y / job->scale.y;
- job->translation.y = job->pageBox.UR.y + job->pageBoundingBox.LL.x / job->scale.x;
+ job->translation.x = -job->pageBox.UR.x - job->pageBoundingBox.LL.x / job->scale.x;
+ job->translation.y = job->pageBox.UR.y + job->pageBoundingBox.LL.y / job->scale.y;
}
else {
job->translation.x = -job->pageBox.LL.x + job->pageBoundingBox.LL.y / job->scale.y;
job->comptrans = job->translation;
job->comptrans.y *= (job->flags & GVRENDER_Y_GOES_DOWN) ? -1: 1;
-
-#if 0
-fprintf(stderr,"pagesArrayElem = %d,%d pageSize = %g,%g pageOffset = %g,%g\n",
- job->pagesArrayElem.x, job->pagesArrayElem.y,
- job->pageSize.x, job->pageSize.y,
- job->pageOffset.x, job->pageOffset.y);
-
-fprintf(stderr,"clip = %g,%g %g,%g pageBox = %g,%g %g,%g\n",
- job->clip.LL.x, job->clip.LL.y,
- job->clip.UR.x, job->clip.UR.y,
- job->pageBox.LL.x, job->pageBox.LL.y,
- job->pageBox.UR.x, job->pageBox.UR.y);
-
-fprintf(stderr,"zoom = %g dpi = %g,%g\n",
- job->zoom,
- job->dpi.x, job->dpi.y);
-#endif
-
gvrender_begin_page(job);
gvrender_set_pencolor(job, DEFAULT_COLOR);
gvrender_set_fillcolor(job, DEFAULT_FILL);
gvc->emit_state = oldstate;
}
-#define EPSILON .0001
-
/* calculate an offset vector, length d, perpendicular to line p,q */
static pointf computeoffset_p(pointf p, pointf q, double d)
{
case GVRENDER_PLUGIN:
job->margin.x = job->margin.y = job->render.features->default_margin;
break;
- case POSTSCRIPT: case PDF: case HPGL: case PCL: case MIF:
- case METAPOST: case FIG: case VTX: case ATTRIBUTED_DOT:
+ case HPGL: case PCL: case MIF: case METAPOST: case VTX: case ATTRIBUTED_DOT:
case PLAIN: case PLAIN_EXT: case QPDF:
job->margin.x = job->margin.y = DEFAULT_PRINT_MARGIN;
break;
case GVRENDER_PLUGIN:
job->dpi = job->render.features->default_dpi;
break;
- case POSTSCRIPT:
- case PDF:
- case SVG:
- job->dpi.x = job->dpi.y = (double)(POINTS_PER_INCH);
- break;
- case FIG:
- job->dpi.x = job->dpi.y = 1200.;
- break;
default:
job->dpi.x = job->dpi.y = (double)(DEFAULT_DPI);
break;
int rv;
assert((GD_bb(g).LL.x == 0) && (GD_bb(g).LL.y == 0));
-
P2PF(GD_bb(g).UR, UR);
/* may want to take this from an attribute someday */
if (GD_drawing(g)->landscape)
UR = exch_xyf(UR);
- /* calculate default viewport size in device units */
- X = (Z * UR.x + (2 * job->pad.x)) * job->dpi.x / POINTS_PER_INCH;
- Y = (Z * UR.y + (2 * job->pad.y)) * job->dpi.y / POINTS_PER_INCH;
+ /* calculate default viewport size in points */
+ X = Z * UR.x + 2 * job->pad.x;
+ Y = Z * UR.y + 2 * job->pad.y;
/* user can override */
if ((str = agget(g, "viewport")))
rv = sscanf(str, "%lf,%lf,%lf,%lf,%lf", &X, &Y, &Z, &x, &y);
/* rv is ignored since args retain previous values if not scanned */
- job->width = ROUND(X);
- job->height = ROUND(Y);
+ job->view.x = X; /* viewport - points */
+ job->view.y = Y;
job->zoom = Z; /* scaling factor */
- job->focus.x = x; /* graph coord of focus - points */
+ job->focus.x = x; /* focus - graph units */
job->focus.y = y;
job->rotation = job->gvc->rotation;
+}
-#if 0
- fprintf(stderr,"bb = %d,%d %d,%d size %d,%d (graph units)\n",
- GD_bb(g).LL.x, GD_bb(g).LL.y,
- GD_bb(g).UR.x, GD_bb(g).UR.y,
- GD_drawing(g)->size.x, GD_drawing(g)->size.y);
- fprintf(stderr,"width,height = %d,%d (device units)\n",
- job->width, job->height);
- fprintf(stderr,"zoom = %g focus = %g,%g (graph units)\n",
- job->zoom, job->focus.x, job->focus.y);
-#endif
+#define EPSILON .0001
+
+/* setup_view is called for a particular device and refresh event.
+ * actual dpi is now known.
+ * width and height might have changed through window resizing
+ */
+static void setup_view(GVJ_t * job, graph_t * g)
+{
+ double sx, sy; /* half width, half height in graph-units */
+
+ /* compute width,height in device units */
+ /* FIXME - width/height also calculated in xlib finalize() using the same formula
+ * to get initial windows size. Should be done in one place only. */
+ job->width = ROUND((job->view.x + 2 * job->margin.x) * job->dpi.x / POINTS_PER_INCH);
+ job->height = ROUND((job->view.y + 2 * job->margin.y) * job->dpi.y / POINTS_PER_INCH);
+
+ job->compscale.x = job->scale.x = job->zoom * job->dpi.x / POINTS_PER_INCH;
+ job->compscale.y = job->scale.y = job->zoom * job->dpi.y / POINTS_PER_INCH;
+ job->compscale.y *= (job->flags & GVRENDER_Y_GOES_DOWN) ? -1. : 1.;
+
+ sx = job->width / (job->scale.x * 2.);
+ sy = job->height / (job->scale.y * 2.);
+
+ /* calculate clip region in graph units
+ * calculate offset to 0,0 of graph in device units */
+ if (job->rotation) {
+ job->clip.UR.x = job->focus.x + sy + EPSILON;
+ job->clip.UR.y = job->focus.y + sx + EPSILON;
+ job->clip.LL.x = job->focus.x - sy - EPSILON;
+ job->clip.LL.y = job->focus.y - sx - EPSILON;
+ job->offset.x = -job->focus.y * job->compscale.x + job->width * 3 / 2;
+ job->offset.y = -job->focus.x * job->compscale.y + job->height / 2.;
+ } else {
+ job->clip.UR.x = job->focus.x + sx + EPSILON;
+ job->clip.UR.y = job->focus.y + sy + EPSILON;
+ job->clip.LL.x = job->focus.x - sx - EPSILON;
+ job->clip.LL.y = job->focus.y - sy - EPSILON;
+ job->offset.x = -job->focus.x * job->compscale.x + job->width / 2.;
+ job->offset.y = -job->focus.y * job->compscale.y + job->height / 2.;
+ }
}
static void emit_colors(GVJ_t * job, graph_t * g)
int flags = job->flags;
GVC_t *gvc = job->gvc;
+ setup_view(job, g);
+
s = late_string(g, agfindattr(g, "comment"), "");
gvrender_comment(job, s);
pointf *headendurl_map_p;
};
+
+/* Note on units:
+ * points - a physical distance (1/72 inch) unaffected by zoom or dpi.
+ * graph units - related to physical distance by zoom. Equals points at zoom=1
+ * device units - related to physical distance in points by dpi/72
+ */
+
struct GVJ_s {
GVC_t *gvc; /* parent gvc */
GVJ_t *next; /* linked list of jobs */
gvdevice_callbacks_t *callbacks;
void *surface; /* gd or cairo surface */
- bool external_surface; /* surface belongs to caller */
+ bool external_surface; /* surface belongs to caller */
gvstyle_t *style; /* active style from gvc->styles[] */
char **rawstyle;
int flags; /* emit_graph flags */
- pointf margin; /* job-specific margin - graph units */
-
int numLayers; /* number of layers */
int layerNum; /* current layer - 1 based*/
- boxf pageBox; /* current page in graph coords */
- pointf pageOffset; /* offset for current page in graph coords */
- pointf pageSize; /* page size in graph units */
- point pagesArraySize; /* 2D size of page array */
- point pagesArrayFirst; /* 2D starting corner in */
- point pagesArrayMajor; /* 2D major increment */
- point pagesArrayMinor; /* 2D minor increment */
- point pagesArrayElem; /* 2D coord of current page - 0,0 based */
- int numPages; /* number of pages */
-
- unsigned int width; /* device width in device units */
- unsigned int height; /* device height in device units */
- box canvasBox; /* drawable region in device units */
- box pageBoundingBox; /* rotated boundingBox in device units */
- box boundingBox; /* cumulative boundingBox over all pages in device units */
+ point pagesArraySize; /* 2D size of page array */
+ point pagesArrayFirst;/* 2D starting corner in */
+ point pagesArrayMajor;/* 2D major increment */
+ point pagesArrayMinor;/* 2D minor increment */
+ point pagesArrayElem; /* 2D coord of current page - 0,0 based */
+ int numPages; /* number of pages */
+
+ boxf bb; /* bb in graph units */
+ pointf pad; /* padding around bb - graph units */
+ boxf clip; /* clip region in graph units */
+ boxf pageBoxClip; /* intersection of clip and pageBox in graph units */
+ boxf pageBox; /* current page in graph units */
+ pointf pageOffset; /* offset for current page in graph units */
+ pointf pageSize; /* page size in graph units */
+ pointf focus; /* viewport focus - graph units */
+
+ double zoom; /* viewport zoom factor (points per graph unit) */
+ int rotation; /* viewport rotation (degrees) 0=portrait, 90=landscape */
+
+ pointf view; /* viewport size - points */
+ box canvasBox; /* drawing area - points */
+ pointf margin; /* job-specific margin - points */
pointf dpi; /* device resolution device-units-per-inch */
- boxf bb; /* bb in graph units */
- pointf pad; /* padding around bb in graph units */
- double zoom; /* viewport zoom factor */
- pointf focus; /* viewport focus in graph units */
-
- boxf clip; /* clip region in graph units */
- boxf pageBoxClip; /* intersection of clip and pageBox */
-
- pointf scale; /* composite device scale (zoom and dpi) */
- int rotation; /* viewport rotation (degrees) 0=portrait, 90=landscape */
- pointf translation; /* composite translation */
-
+ unsigned int width; /* device width - device units */
+ unsigned int height; /* device height - device units */
+ box pageBoundingBox;/* rotated boundingBox - device units */
+ box boundingBox; /* cumulative boundingBox over all pages - device units */
- pointf compscale; /* composite device scale incl: zoom, dpi, y_goes_down */
- pointf comptrans; /* composite translation */
- pointf offset; /* composite translation (used by codegens) */
+ pointf scale; /* composite device scale (zoom and dpi) */
+ pointf translation; /* composite translation */
+ pointf compscale; /* composite device scale incl: zoom, dpi, y_goes_down */
+ pointf comptrans; /* composite translation */
+ pointf offset; /* composite translation (used by codegens) */
bool fit_mode,
needs_refresh,