Corrected a long-standing error in which ending text with a literal
[xcircuit.git] / cairo.c
blobbbdd843331059a009e6bcd4d4de6877a4e6e6669
1 /*-------------------------------------------------------------------------*/
2 /* cairo.c --- mainly cairo versions of the UDraw... stuff in functions.c */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-------------------------------------------------------------------------*/
5 /*-------------------------------------------------------------------------*/
6 /* originally written by Tim Edwards, 8/13/93 */
7 /* All cairo graphics library modifications by Erik van der Wal, May 2014 */
8 /*-------------------------------------------------------------------------*/
10 #ifdef HAVE_CAIRO
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <stdint.h>
15 #include <assert.h>
16 #include <math.h>
17 #include <limits.h>
19 #ifndef XC_WIN32
20 #include <X11/Intrinsic.h>
21 #endif
23 #ifdef TCL_WRAPPER
24 #include <tk.h>
25 #endif /* TCL_WRAPPER */
27 #include "xcircuit.h"
28 #include "colordefs.h"
29 #include "prototypes.h"
31 #ifdef CAIRO_HAS_FC_FONT
32 #include <cairo/cairo-ft.h>
33 #endif /* CAIRO_HAS_FC_FONT */
35 #ifdef HAVE_GS
36 #include <ghostscript/ierrors.h>
37 #include <ghostscript/iapi.h>
38 #include <ghostscript/gdevdsp.h>
39 #endif /* HAVE_GS */
41 extern XCWindowData *areawin;
42 extern Globaldata xobjs;
43 extern int number_colors;
44 extern colorindex *colorlist;
45 extern short fontcount;
46 extern fontinfo *fonts;
47 extern Cursor appcursors[NUM_CURSORS];
48 extern Display *dpy;
49 extern gs_state_t gs_state;
50 extern cairo_surface_t *bbuf;
52 static cairo_user_data_key_t fontinfo_key;
54 static void xc_cairo_strokepath(short style, float width);
56 /*----------------------------------------------------------------------------*/
58 void xc_cairo_set_matrix(const Matrix *xcm)
60 cairo_matrix_t m;
61 m.xx = xcm->a; m.xy = xcm->b; m.x0 = xcm->c;
62 m.yx = xcm->d; m.yy = xcm->e; m.y0 = xcm->f;
63 cairo_set_matrix(areawin->cr, &m);
66 /*----------------------------------------------------------------------*/
67 /* Set the color, based on the given color index */
68 /*----------------------------------------------------------------------*/
70 void xc_cairo_set_color(int coloridx)
72 colorindex *xcc;
74 if (coloridx >= number_colors) return;
76 xcc = &colorlist[coloridx];
77 cairo_set_source_rgb(areawin->cr,
78 (double)xcc->color.red / 65535.,
79 (double)xcc->color.green / 65535.,
80 (double)xcc->color.blue / 65535.);
83 /*----------------------------------------------------------------------------*/
85 void UDrawLine(XPoint *pt1, XPoint *pt2)
87 if (!areawin->redraw_ongoing) {
88 areawin->redraw_needed = True;
89 return;
92 cairo_save(areawin->cr);
94 cairo_set_line_width(areawin->cr, xobjs.pagelist[areawin->page]->wirewidth);
95 cairo_set_dash(areawin->cr, NULL, 0, 0.);
96 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_ROUND);
97 cairo_set_line_join(areawin->cr, CAIRO_LINE_JOIN_BEVEL);
99 cairo_move_to(areawin->cr, pt1->x, pt1->y);
100 cairo_line_to(areawin->cr, pt2->x, pt2->y);
101 cairo_stroke(areawin->cr);
103 cairo_restore(areawin->cr);
106 /*----------------------------------------------------------------------*/
107 /* Add circle at given point to indicate that the point is a parameter. */
108 /* The circle is divided into quarters. For parameterized y-coordinate */
109 /* the top and bottom quarters are drawn. For parameterized x- */
110 /* coordinate, the left and right quarters are drawn. A full circle */
111 /* indicates either both x- and y-coordinates are parameterized, or */
112 /* else any other kind of parameterization (presently, not used). */
113 /* */
114 /* (note that the two angles in XDrawArc() are 1) the start angle, */
115 /* measured in absolute 64th degrees from 0 (3 o'clock), and 2) the */
116 /* path length, in relative 64th degrees (positive = counterclockwise, */
117 /* negative = clockwise)). */
118 /*----------------------------------------------------------------------*/
120 void UDrawCircle(XPoint *upt, u_char which)
122 XPoint wpt;
124 if (!areawin->redraw_ongoing) {
125 areawin->redraw_needed = True;
126 return;
129 cairo_save(areawin->cr);
130 cairo_identity_matrix(areawin->cr);
132 user_to_window(*upt, &wpt);
133 cairo_set_line_width(areawin->cr, .75);
134 cairo_set_dash(areawin->cr, NULL, 0, 0.);
135 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_BUTT);
136 cairo_set_line_join(areawin->cr, CAIRO_LINE_JOIN_MITER);
138 /* TODO: angles might be mirrored or turning the wrong way */
139 switch(which) {
140 case P_POSITION_X:
141 cairo_arc(areawin->cr, wpt.x, wpt.y, 4., M_PI * -.25, M_PI * .25);
142 cairo_arc(areawin->cr, wpt.x, wpt.y, 4., M_PI * .75, M_PI * 1.25);
143 break;
144 case P_POSITION_Y:
145 cairo_arc(areawin->cr, wpt.x, wpt.y, 4., M_PI * .25, M_PI * .75);
146 cairo_arc(areawin->cr, wpt.x, wpt.y, 4., M_PI * 1.25, M_PI * 1.75);
147 break;
148 default:
149 cairo_arc(areawin->cr, wpt.x, wpt.y, 4., 0., M_PI * 2.);
150 break;
153 cairo_restore(areawin->cr);
156 /*----------------------------------------------------------------------*/
157 /* Add "X" at string origin */
158 /*----------------------------------------------------------------------*/
160 void UDrawXAt(XPoint *wpt)
162 if (!areawin->redraw_ongoing) {
163 areawin->redraw_needed = True;
164 return;
167 cairo_save(areawin->cr);
168 cairo_identity_matrix(areawin->cr);
170 cairo_set_dash(areawin->cr, NULL, 0, 0.);
171 cairo_set_line_width(areawin->cr, .75);
173 cairo_move_to(areawin->cr, wpt->x - 3., wpt->y - 3.);
174 cairo_line_to(areawin->cr, wpt->x + 3., wpt->y + 3.);
175 cairo_move_to(areawin->cr, wpt->x + 3., wpt->y - 3.);
176 cairo_line_to(areawin->cr, wpt->x - 3., wpt->y + 3.);
177 cairo_stroke(areawin->cr);
179 cairo_restore(areawin->cr);
182 void UDrawXLine(XPoint opt, XPoint cpt)
184 XPoint upt, vpt;
185 double dashes[] = {4., 4.};
187 if (!areawin->redraw_ongoing) {
188 areawin->redraw_needed = True;
189 return;
192 cairo_save(areawin->cr);
193 cairo_identity_matrix(areawin->cr);
195 xc_cairo_set_color(AUXCOLOR);
196 cairo_set_dash(areawin->cr, dashes, sizeof(dashes) / sizeof(double), 0.);
197 cairo_set_line_width(areawin->cr, .75);
199 user_to_window(cpt, &upt);
200 user_to_window(opt, &vpt);
201 cairo_move_to(areawin->cr, vpt.x, vpt.y);
202 cairo_line_to(areawin->cr, upt.x, upt.y);
203 cairo_stroke(areawin->cr);
205 cairo_set_dash(areawin->cr, NULL, 0, 0.);
206 cairo_move_to(areawin->cr, upt.x - 3., upt.y - 3.);
207 cairo_line_to(areawin->cr, upt.x + 3., upt.y + 3.);
208 cairo_move_to(areawin->cr, upt.x + 3., upt.y - 3.);
209 cairo_line_to(areawin->cr, upt.x - 3., upt.y + 3.);
210 cairo_stroke(areawin->cr);
212 cairo_restore(areawin->cr);
215 /*-------------------------------------------------------------------------*/
217 void UDrawBox(XPoint origin, XPoint corner)
219 XPoint worig, wcorn;
220 double r, g, b, a;
222 if (!areawin->redraw_ongoing) {
223 areawin->redraw_needed = True;
224 return;
227 user_to_window(origin, &worig);
228 user_to_window(corner, &wcorn);
230 cairo_save(areawin->cr);
231 cairo_identity_matrix(areawin->cr);
233 cairo_set_line_width(areawin->cr, 1.0);
234 cairo_set_dash(areawin->cr, NULL, 0, 0.);
235 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_SQUARE);
236 cairo_set_line_join(areawin->cr, CAIRO_LINE_JOIN_MITER);
238 cairo_move_to(areawin->cr, worig.x + .5, worig.y + .5);
239 cairo_line_to(areawin->cr, worig.x + .5, wcorn.y + .5);
240 cairo_line_to(areawin->cr, wcorn.x + .5, wcorn.y + .5);
241 cairo_line_to(areawin->cr, wcorn.x + .5, worig.y + .5);
242 cairo_close_path(areawin->cr);
244 xc_cairo_set_color(AUXCOLOR);
245 cairo_pattern_get_rgba(cairo_get_source(areawin->cr), &r, &g, &b, &a);
246 cairo_set_source_rgba(areawin->cr, r, g, b, .1 * a);
247 cairo_fill_preserve(areawin->cr);
248 cairo_set_source_rgba(areawin->cr, r, g, b, a);
249 cairo_stroke(areawin->cr);
251 cairo_restore(areawin->cr);
254 /*----------------------------------------------------------------------*/
255 /* Draw a box indicating the dimensions of the edit element that most */
256 /* closely reach the position "corner". */
257 /*----------------------------------------------------------------------*/
259 void UDrawRescaleBox(XPoint *corner)
261 XPoint newpoints[5];
263 if (!areawin->redraw_ongoing) {
264 areawin->redraw_needed = True;
265 /* No return here, since the return value might be needed? */
268 if (areawin->selects == 0)
269 return;
271 if (areawin->redraw_ongoing) {
272 int i;
273 UGetRescaleBox(corner, newpoints);
275 cairo_save(areawin->cr);
276 xc_cairo_set_color(AUXCOLOR);
277 cairo_set_dash(areawin->cr, NULL, 0, 0.);
278 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_ROUND);
279 cairo_set_line_join(areawin->cr, CAIRO_LINE_JOIN_BEVEL);
280 cairo_move_to(areawin->cr, newpoints[0].x, newpoints[0].y);
281 for (i = 1; i < 4; i++)
282 cairo_line_to(areawin->cr, newpoints[i].x, newpoints[i].y);
283 xc_cairo_strokepath(0, 1);
284 cairo_restore(areawin->cr);
288 /*-------------------------------------------------------------------------*/
290 void UDrawBBox()
292 XPoint origin;
293 XPoint worig, wcorn, corner;
294 objinstptr bbinst = areawin->topinstance;
296 if (!areawin->redraw_ongoing) {
297 areawin->redraw_needed = True;
298 return;
301 if ((!areawin->bboxon) || (checkforbbox(topobject) != NULL)) return;
303 origin = bbinst->bbox.lowerleft;
304 corner.x = origin.x + bbinst->bbox.width;
305 corner.y = origin.y + bbinst->bbox.height;
307 /* Include any schematic labels in the bounding box. */
308 extendschembbox(bbinst, &origin, &corner);
310 user_to_window(origin, &worig);
311 user_to_window(corner, &wcorn);
313 cairo_save(areawin->cr);
314 cairo_identity_matrix(areawin->cr);
316 xc_cairo_set_color(BBOXCOLOR);
317 cairo_set_line_width(areawin->cr, 1.0);
318 cairo_set_dash(areawin->cr, NULL, 0, 0.);
319 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_SQUARE);
320 cairo_set_line_join(areawin->cr, CAIRO_LINE_JOIN_MITER);
322 cairo_move_to(areawin->cr, worig.x + .5, worig.y + .5);
323 cairo_line_to(areawin->cr, worig.x + .5, wcorn.y + .5);
324 cairo_line_to(areawin->cr, wcorn.x + .5, wcorn.y + .5);
325 cairo_line_to(areawin->cr, wcorn.x + .5, worig.y + .5);
326 cairo_close_path(areawin->cr);
327 cairo_stroke(areawin->cr);
329 cairo_restore(areawin->cr);
332 /*----------------------------------------------------------------------*/
333 /* Main recursive object instance drawing routine. */
334 /* context is the instance information passed down from above */
335 /* theinstance is the object instance to be drawn */
336 /* level is the level of recursion */
337 /* passcolor is the inherited color value passed to object */
338 /* passwidth is the inherited linewidth value passed to the object */
339 /* stack contains graphics context information */
340 /*----------------------------------------------------------------------*/
342 void UDrawObject(objinstptr theinstance, short level, int passcolor,
343 float passwidth, pushlistptr *stack)
345 genericptr *areagen;
346 float tmpwidth;
347 int defaultcolor = passcolor;
348 int curcolor = passcolor;
349 int thispart;
350 short savesel;
351 XPoint bboxin[2], bboxout[2];
352 u_char xm, ym;
353 objectptr theobject = theinstance->thisobject;
355 if (!areawin->redraw_ongoing) {
356 areawin->redraw_needed = True;
357 return;
360 /* Save the number of selections and set it to zero while we do the */
361 /* object drawing. */
363 savesel = areawin->selects;
364 areawin->selects = 0;
366 /* All parts are given in the coordinate system of the object, unless */
367 /* this is the top-level object, in which they will be interpreted as */
368 /* relative to the screen. */
370 UPushCTM();
372 /* Stack is not used by cairo but *is* used by expression evaluators */
373 if (stack) push_stack((pushlistptr *)stack, theinstance, (char *)NULL);
375 if (level != 0)
376 UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
377 theinstance->rotation);
379 if (theinstance->style & LINE_INVARIANT)
380 passwidth /= fabs(theinstance->scale);
382 /* do a quick test for intersection with the display window */
384 bboxin[0].x = theobject->bbox.lowerleft.x;
385 bboxin[0].y = theobject->bbox.lowerleft.y;
386 bboxin[1].x = theobject->bbox.lowerleft.x + theobject->bbox.width;
387 bboxin[1].y = theobject->bbox.lowerleft.y + theobject->bbox.height;
388 if (level == 0)
389 extendschembbox(theinstance, &(bboxin[0]), &(bboxin[1]));
390 UTransformbyCTM(DCTM, bboxin, bboxout, 2);
392 xm = (bboxout[0].x < bboxout[1].x) ? 0 : 1;
393 ym = (bboxout[0].y < bboxout[1].y) ? 0 : 1;
395 if (bboxout[xm].x < areawin->width && bboxout[ym].y < areawin->height &&
396 bboxout[1 - xm].x > 0 && bboxout[1 - ym].y > 0) {
398 /* make parameter substitutions */
399 psubstitute(theinstance);
401 /* draw all of the elements */
403 tmpwidth = UTopTransScale(passwidth);
404 cairo_set_line_width(areawin->cr, tmpwidth);
405 cairo_set_dash(areawin->cr, NULL, 0, 0.);
406 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_ROUND);
407 cairo_set_line_join(areawin->cr, CAIRO_LINE_JOIN_BEVEL);
409 /* guard against plist being regenerated during a redraw by the */
410 /* expression parameter mechanism (should that be prohibited?) */
412 for (thispart = 0; thispart < theobject->parts; thispart++) {
413 areagen = theobject->plist + thispart;
414 if ((*areagen)->type & DRAW_HIDE) continue;
416 if (defaultcolor != DOFORALL) {
417 if ((*areagen)->color != curcolor) {
418 if ((*areagen)->color == DEFAULTCOLOR)
419 curcolor = defaultcolor;
420 else
421 curcolor = (*areagen)->color;
423 XcTopSetForeground(curcolor);
427 switch(ELEMENTTYPE(*areagen)) {
428 case(POLYGON):
429 if (level == 0 || !((TOPOLY(areagen))->style & BBOX))
430 UDrawPolygon(TOPOLY(areagen), passwidth);
431 break;
433 case(SPLINE):
434 UDrawSpline(TOSPLINE(areagen), passwidth);
435 break;
437 case(ARC):
438 UDrawArc(TOARC(areagen), passwidth);
439 break;
441 case(PATH):
442 UDrawPath(TOPATH(areagen), passwidth);
443 break;
445 case(GRAPHIC):
446 UDrawGraphic(TOGRAPHIC(areagen));
447 break;
449 case(OBJINST):
450 UDrawObject(TOOBJINST(areagen), level + 1, curcolor, passwidth, stack);
451 break;
453 case(LABEL):
454 if (level == 0 || TOLABEL(areagen)->pin == False)
455 UDrawString(TOLABEL(areagen), curcolor, theinstance);
456 else if ((TOLABEL(areagen)->anchor & PINVISIBLE) && areawin->pinpointon)
457 UDrawString(TOLABEL(areagen), curcolor, theinstance);
458 else if (TOLABEL(areagen)->anchor & PINVISIBLE)
459 UDrawStringNoX(TOLABEL(areagen), curcolor, theinstance);
460 else if (level == 1 && TOLABEL(areagen)->pin &&
461 TOLABEL(areagen)->pin != INFO && areawin->pinpointon)
462 UDrawXDown(TOLABEL(areagen));
463 break;
467 /* restore the color passed to the object, if different from current color */
469 if ((defaultcolor != DOFORALL) && (passcolor != curcolor)) {
470 XTopSetForeground(passcolor);
474 /* restore the selection list (if any) */
475 areawin->selects = savesel;
476 UPopCTM();
478 /* Stack is not used by cairo but *is* used by expression evaluators */
479 if (stack) pop_stack(stack);
482 /*-------------------------------------------------------------------------*/
484 static void xc_cairo_strokepath(short style, float width)
486 if (!(style & CLIPMASK) || (areawin->showclipmasks == TRUE)) {
487 if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) {
488 if ((style & FILLSOLID) == FILLSOLID)
489 cairo_fill_preserve(areawin->cr);
490 else {
491 double red, green, blue, alpha;
492 cairo_pattern_get_rgba(cairo_get_source(areawin->cr),
493 &red, &green, &blue, &alpha);
494 if (!(style & FILLED))
495 cairo_set_source_rgba(areawin->cr, 1., 1., 1., alpha);
496 else {
497 double m = (1 + ((style & FILLSOLID) >> 5)) / 8.;
498 if (style & OPAQUE) {
499 double n = (1. - m);
500 cairo_set_source_rgba(areawin->cr, m * red + n,
501 m * green + n, m * blue + n, alpha);
503 else
504 cairo_set_source_rgba(areawin->cr, red, green, blue,
505 m * alpha);
507 cairo_fill_preserve(areawin->cr);
508 cairo_set_source_rgba(areawin->cr, red, green, blue, alpha);
511 if (!(style & NOBORDER)) {
512 cairo_set_line_width(areawin->cr, width);
513 cairo_set_line_join(areawin->cr, (style & SQUARECAP) ?
514 CAIRO_LINE_JOIN_MITER : CAIRO_LINE_JOIN_BEVEL);
515 if (style & (DASHED | DOTTED)) {
516 double dashes[2];
517 dashes[0] = dashes[1] = 4.0 * width;
518 if (style & DOTTED)
519 dashes[0] = width;
520 cairo_set_dash(areawin->cr, dashes, 2, 0.0);
521 cairo_set_line_width(areawin->cr, width);
522 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_BUTT);
524 else {
525 cairo_set_dash(areawin->cr, NULL, 0, 0.0);
526 cairo_set_line_cap(areawin->cr, (style & SQUARECAP) ?
527 CAIRO_LINE_CAP_SQUARE : CAIRO_LINE_CAP_ROUND);
530 /* draw the spline and close off if so specified */
531 if (!(style & UNCLOSED))
532 cairo_close_path(areawin->cr);
533 cairo_stroke_preserve(areawin->cr);
536 if (style & CLIPMASK)
537 cairo_clip_preserve(areawin->cr);
538 cairo_new_path(areawin->cr); /* clear preserved paths */
541 /*-------------------------------------------------------------------------*/
543 void UDrawPolygon(polyptr thepoly, float passwidth)
545 int i;
547 if (!areawin->redraw_ongoing) {
548 areawin->redraw_needed = True;
549 return;
552 if (thepoly->number) {
553 cairo_move_to(areawin->cr, thepoly->points[0].x, thepoly->points[0].y);
554 for (i = 1; i < thepoly->number; i++)
555 cairo_line_to(areawin->cr, thepoly->points[i].x, thepoly->points[i].y);
556 xc_cairo_strokepath(thepoly->style, thepoly->width * passwidth);
560 /*-------------------------------------------------------------------------*/
562 void UDrawArc(arcptr thearc, float passwidth)
564 if (!areawin->redraw_ongoing) {
565 areawin->redraw_needed = True;
566 return;
569 if (abs(thearc->radius) == thearc->yaxis)
570 cairo_arc(areawin->cr, thearc->position.x, thearc->position.y,
571 abs(thearc->radius), thearc->angle1 * M_PI / 180.0,
572 thearc->angle2 * M_PI / 180.0);
573 else if (thearc->yaxis) {
574 /* perform elliptical arc, as described in cairo manual */
575 cairo_save(areawin->cr);
576 cairo_translate(areawin->cr, thearc->position.x, thearc->position.y);
577 cairo_scale(areawin->cr, abs(thearc->radius), thearc->yaxis);
578 cairo_arc(areawin->cr, 0.0, 0.0, 1.0, thearc->angle1 * M_PI / 180.0,
579 thearc->angle2 * M_PI / 180.0);
580 cairo_restore(areawin->cr);
582 else { /* no y-axis dimension: draw a line */
583 /* can we do this in a more elegant manner? */
584 double theta;
585 double theta_start = thearc->angle1 * RADFAC;
586 double theta_stop = thearc->angle2 * RADFAC;
587 cairo_move_to(areawin->cr, thearc->position.x + fabs(thearc->radius)
588 * cos(theta_start), thearc->position.y);
589 for (theta = -M_PI; theta < theta_stop; theta += M_PI) {
590 if (theta <= theta_start) continue;
591 cairo_line_to(areawin->cr, thearc->position.x
592 + fabs(thearc->radius) * cos(theta), thearc->position.y);
594 cairo_line_to(areawin->cr, thearc->position.x + fabs(thearc->radius)
595 * cos(theta_stop), thearc->position.y);
597 xc_cairo_strokepath(thearc->style, thearc->width * passwidth);
600 /*-------------------------------------------------------------------------*/
602 void UDrawPath(pathptr thepath, float passwidth)
604 genericptr *genpath;
605 polyptr thepoly;
606 splineptr thespline;
607 XPoint *xp;
609 if (!areawin->redraw_ongoing) {
610 areawin->redraw_needed = True;
611 return;
614 /* Draw first point */
615 if (thepath->parts) {
616 genpath = thepath->plist;
617 switch(ELEMENTTYPE(*genpath)) {
618 case POLYGON:
619 thepoly = TOPOLY(genpath);
620 cairo_move_to(areawin->cr, thepoly->points[0].x,
621 thepoly->points[0].y);
622 xp = &thepoly->points[0];
623 break;
624 case SPLINE:
625 thespline = TOSPLINE(genpath);
626 cairo_move_to(areawin->cr, thespline->ctrl[0].x,
627 thespline->ctrl[0].y);
628 xp = &thespline->ctrl[0];
629 break;
632 /* Draw all other points */
633 for (genpath = thepath->plist; genpath < thepath->plist + thepath->parts;
634 genpath++) {
635 int i;
637 switch(ELEMENTTYPE(*genpath)) {
638 case POLYGON:
639 thepoly = TOPOLY(genpath);
640 for (i = 1; i < thepoly->number; i++)
641 cairo_line_to(areawin->cr, thepoly->points[i].x,
642 thepoly->points[i].y);
643 xp = &thepoly->points[thepoly->number - 1];
644 break;
645 case SPLINE:
646 thespline = TOSPLINE(genpath);
648 /* If the curve 1st point is not the same as the last point
649 * drawn, then first draw a line to the first curve point.
652 if ((thespline->ctrl[0].x != xp->x) || (thespline->ctrl[0].y != xp->y))
653 cairo_line_to(areawin->cr, thespline->ctrl[0].x,
654 thespline->ctrl[0].y);
656 cairo_curve_to(areawin->cr, thespline->ctrl[1].x,
657 thespline->ctrl[1].y, thespline->ctrl[2].x,
658 thespline->ctrl[2].y, thespline->ctrl[3].x,
659 thespline->ctrl[3].y);
660 xp = &thespline->ctrl[3];
661 break;
664 xc_cairo_strokepath(thepath->style, thepath->width * passwidth);
667 /*-------------------------------------------------------------------------*/
669 void UDrawSpline(splineptr thespline, float passwidth)
671 if (!areawin->redraw_ongoing) {
672 areawin->redraw_needed = True;
673 return;
676 cairo_move_to(areawin->cr, thespline->ctrl[0].x, thespline->ctrl[0].y);
677 cairo_curve_to(areawin->cr, thespline->ctrl[1].x, thespline->ctrl[1].y,
678 thespline->ctrl[2].x, thespline->ctrl[2].y,
679 thespline->ctrl[3].x, thespline->ctrl[3].y);
680 xc_cairo_strokepath(thespline->style, thespline->width * passwidth);
683 /*-------------------------------------------------------------------------*/
685 void UDrawGraphic(graphicptr gp)
687 double w, h;
689 if (!areawin->redraw_ongoing) {
690 areawin->redraw_needed = True;
691 return;
694 cairo_save(areawin->cr);
695 cairo_translate(areawin->cr,
696 gp->position.x,
697 gp->position.y);
698 cairo_rotate(areawin->cr, -gp->rotation * RADFAC);
699 cairo_scale(areawin->cr, gp->scale, -gp->scale);
700 w = cairo_image_surface_get_width(gp->source);
701 h = cairo_image_surface_get_height(gp->source);
702 cairo_set_source_surface(areawin->cr, gp->source,
703 -w / 2., -h / 2.);
704 cairo_rectangle(areawin->cr, -w / 2, -h / 2, w, h);
705 cairo_clip(areawin->cr);
706 cairo_paint(areawin->cr);
707 cairo_restore(areawin->cr);
710 /*----------------------------*/
711 /* Draw the grids, axis, etc. */
712 /*----------------------------*/
714 void draw_grids(void)
716 double spc, spc2, spc3;
717 cairo_matrix_t m = {1., 0., 0., -1., 0., 0.};
718 m.x0 = -areawin->pcorner.x * areawin->vscale;
719 m.y0 = areawin->height + areawin->pcorner.y * areawin->vscale;
721 if (!areawin->redraw_ongoing) {
722 areawin->redraw_needed = True;
723 return;
726 cairo_save(areawin->cr);
728 /* draw lines for grid */
729 spc = xobjs.pagelist[areawin->page]->gridspace * areawin->vscale;
730 if (areawin->gridon && spc > 8) {
731 double x, y;
732 int ix, iy;
733 /* find bottom-right point on the grid */
734 double xbegin = areawin->width;
735 double ybegin = areawin->height;
736 cairo_set_matrix(areawin->cr, &m);
737 cairo_scale(areawin->cr, spc, spc);
738 cairo_device_to_user(areawin->cr, &xbegin, &ybegin);
739 xbegin = floor(xbegin);
740 ybegin = ceil(ybegin);
741 ix = xbegin;
742 iy = ybegin;
743 cairo_user_to_device(areawin->cr, &xbegin, &ybegin);
744 cairo_identity_matrix(areawin->cr);
745 /* draw the grid */
746 xc_cairo_set_color(GRIDCOLOR);
747 cairo_set_line_width(areawin->cr, 1.);
748 for (x = xbegin; x >= 0.; x -= spc, ix--) {
749 if (!ix && areawin->axeson) continue; /* do not draw main axis */
750 cairo_move_to(areawin->cr, floor(x) + .5, .5);
751 cairo_line_to(areawin->cr, floor(x) + .5, areawin->height + .5);
753 for (y = ybegin; y >= 0.; y -= spc, iy++) {
754 if (!iy && areawin->axeson) continue; /* do not draw main axis */
755 cairo_move_to(areawin->cr, .5, floor(y) + .5);
756 cairo_line_to(areawin->cr, areawin->width + .5, floor(y) + .5);
758 cairo_stroke(areawin->cr);
762 if (areawin->axeson) {
763 /* find main axis */
764 double x = 0, y = 0;
765 cairo_set_matrix(areawin->cr, &m);
766 cairo_user_to_device(areawin->cr, &x, &y);
767 cairo_identity_matrix(areawin->cr);
768 /* draw the grid */
769 xc_cairo_set_color(AXESCOLOR);
770 cairo_set_line_width(areawin->cr, 1.);
771 cairo_move_to(areawin->cr, floor(x) + .5, .5);
772 cairo_line_to(areawin->cr, floor(x) + .5, areawin->height + .5);
773 cairo_move_to(areawin->cr, .5, floor(y) + .5);
774 cairo_line_to(areawin->cr, areawin->width + .5, floor(y) + .5);
775 cairo_stroke(areawin->cr);
778 /* bounding box goes beneath everything except grid/axis lines */
779 UDrawBBox();
781 /* draw a little red dot at each snap-to point */
782 spc2 = xobjs.pagelist[areawin->page]->snapspace * areawin->vscale;
783 if (areawin->snapto && spc2 > 8) {
784 double x, y;
785 /* find bottom-right point on the grid */
786 double xbegin = areawin->width;
787 double ybegin = areawin->height;
788 cairo_set_matrix(areawin->cr, &m);
789 cairo_scale(areawin->cr, spc2, spc2);
790 cairo_device_to_user(areawin->cr, &xbegin, &ybegin);
791 xbegin = floor(xbegin);
792 ybegin = ceil(ybegin);
793 cairo_user_to_device(areawin->cr, &xbegin, &ybegin);
794 cairo_identity_matrix(areawin->cr);
795 /* draw the grid */
796 xc_cairo_set_color(SNAPCOLOR);
797 cairo_set_line_width(areawin->cr, 1.);
798 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_ROUND);
799 for (x = xbegin; x >= 0.; x -= spc2) {
800 for (y = ybegin; y >= 0.; y -= spc2) {
801 cairo_move_to(areawin->cr, floor(x) + .5, floor(y) + .5);
802 cairo_close_path(areawin->cr);
805 cairo_stroke(areawin->cr);
808 /* Draw major snap points */
809 spc3 = spc * 20.;
810 if (spc > 4.) {
811 double x, y;
812 /* find bottom-right point on the grid */
813 double xbegin = areawin->width;
814 double ybegin = areawin->height;
815 cairo_set_matrix(areawin->cr, &m);
816 cairo_scale(areawin->cr, spc3, spc3);
817 cairo_device_to_user(areawin->cr, &xbegin, &ybegin);
818 xbegin = floor(xbegin);
819 ybegin = ceil(ybegin);
820 cairo_user_to_device(areawin->cr, &xbegin, &ybegin);
821 cairo_identity_matrix(areawin->cr);
822 /* draw the grid */
823 xc_cairo_set_color(GRIDCOLOR);
824 cairo_set_line_width(areawin->cr, 3.);
825 cairo_set_line_cap(areawin->cr, CAIRO_LINE_CAP_ROUND);
826 for (x = xbegin; x >= 0.; x -= spc3) {
827 for (y = ybegin; y >= 0.; y -= spc3) {
828 cairo_move_to(areawin->cr, floor(x) + .5, floor(y) + .5);
829 cairo_close_path(areawin->cr);
832 cairo_stroke(areawin->cr);
834 cairo_restore(areawin->cr);
837 /*---------------------------------------------------------------------*/
838 /* Draw a string at position offset, starting at the start index and */
839 /* ending before the end index. The offset is updated on return. */
840 /*---------------------------------------------------------------------*/
842 void UDrawCharString(u_char *text, int start, int end, XfPoint *offset,
843 short styles, short ffont, int groupheight, int passcolor, float tmpscale)
845 int idx, nglyphs = 0;
846 cairo_matrix_t oldm;
847 double x, y;
848 cairo_glyph_t *glyphs;
849 UNUSED(passcolor);
851 cairo_get_matrix(areawin->cr, &oldm);
852 cairo_scale(areawin->cr, tmpscale, fabs(tmpscale));
854 /* under- and overlines */
855 if (styles & 8)
856 y = offset->y / tmpscale - 6.;
857 else if (styles & 16)
858 y = offset->y / tmpscale + groupheight + 4;
859 if (styles & 24) {
860 /* under/overline thickness scales if bold */
861 float tmpthick = ((fonts[ffont].flags & 0x21) == 0x21) ? 4.0 : 2.0;
862 cairo_set_line_width(areawin->cr, tmpthick);
864 x = offset->x / tmpscale;
867 glyphs = cairo_glyph_allocate(end - start);
868 for (idx = start; idx < end; idx++)
870 /* Add glyphs */
871 glyphs[nglyphs].index = fonts[ffont].glyph_index[text[idx]];
872 glyphs[nglyphs].x = offset->x / tmpscale;
873 glyphs[nglyphs].y = offset->y / fabs(tmpscale);
874 nglyphs++;
875 /* Determine character width */
876 offset->x += fonts[ffont].glyph_advance[text[idx]] * tmpscale;
878 if (nglyphs) {
879 cairo_show_glyphs(areawin->cr, glyphs, nglyphs);
880 cairo_new_path(areawin->cr);
882 cairo_glyph_free(glyphs);
884 if (styles & 24) {
885 cairo_move_to(areawin->cr, x, y);
886 cairo_line_to(areawin->cr, offset->x / tmpscale, y);
887 cairo_stroke(areawin->cr);
889 cairo_set_matrix(areawin->cr, &oldm);
892 /*----------------------------------------------------------------------*/
893 /* A completely stripped down version of UDrawObject, used for stroke */
894 /* font drawing in xc_user_font_render */
895 /*----------------------------------------------------------------------*/
897 static void xc_draw_glyph_object(objinstptr inst, float passwidth)
899 int thispart;
900 genericptr *areagen;
901 objectptr obj = inst->thisobject;
902 objinstptr ptr;
904 for (thispart = 0; thispart < obj->parts; thispart++) {
905 areagen = obj->plist + thispart;
906 switch(ELEMENTTYPE(*areagen)) {
907 case(POLYGON):
908 UDrawPolygon(TOPOLY(areagen), passwidth);
909 break;
910 case(SPLINE):
911 UDrawSpline(TOSPLINE(areagen), passwidth);
912 break;
913 case(ARC):
914 UDrawArc(TOARC(areagen), passwidth);
915 break;
916 case(PATH):
917 UDrawPath(TOPATH(areagen), passwidth);
918 break;
919 case(OBJINST):
920 ptr = TOOBJINST(areagen);
921 cairo_save(areawin->cr);
922 cairo_translate(areawin->cr, ptr->position.x, ptr->position.y);
923 cairo_rotate(areawin->cr, -ptr->rotation * RADFAC);
924 cairo_scale(areawin->cr, ptr->scale, fabs(ptr->scale));
925 xc_draw_glyph_object(ptr, passwidth);
926 cairo_restore(areawin->cr);
927 break;
932 /*----------------------------------------------------------------------*/
933 /* Rendering function for a cairo_user_font_face, to draw the stroke */
934 /* fonts of xcircuit */
935 /* TODO: always this factor 40 to match with cairo. Is it BASELINE? */
936 /*----------------------------------------------------------------------*/
938 static cairo_status_t xc_user_font_render(cairo_scaled_font_t *scaled_font,
939 unsigned long glyph, cairo_t *cr, cairo_text_extents_t *extents)
941 objectptr chr;
942 objinst charinst;
943 objinstptr theinstance;
944 cairo_t *old_cr;
945 float passwidth;
946 double llx, lly, trx, try;
948 cairo_font_face_t *ff = cairo_scaled_font_get_font_face(scaled_font);
949 size_t fontidx = (size_t) cairo_font_face_get_user_data(ff, &fontinfo_key);
950 fontinfo *fi = &fonts[fontidx];
952 chr = fi->encoding[glyph];
953 theinstance = &charinst;
954 charinst.thisobject = chr;
956 llx = chr->bbox.lowerleft.x / 40.;
957 lly = chr->bbox.lowerleft.y / 40.;
958 trx = (chr->bbox.lowerleft.x + chr->bbox.width) / 40.;
959 try = (chr->bbox.lowerleft.y + chr->bbox.height) / 40.;
961 /* temporary override areawin->cr with the font context */
962 old_cr = areawin->cr;
963 areawin->cr = cr;
965 cairo_scale(cr, 1. / 40., -1. / 40.);
966 cairo_set_line_width(cr, 1);
968 /* if font is derived and italic, premultiply by slanting matrix */
969 if ((fi->flags & 0x22) == 0x22) {
970 cairo_matrix_t m = {1., 0., .25, 1., 0., 0.};
971 cairo_transform(areawin->cr, &m);
972 llx += .25 * lly;
973 trx += .25 * try;
975 /* simple boldface technique for derived fonts */
976 passwidth = ((fi->flags & 0x21) == 0x21) ? 4. : 2.;
978 /* Correct extentswith line width */
979 llx -= passwidth / 40.;
980 lly -= passwidth / 40.;
981 trx += passwidth / 40.;
982 try += passwidth / 40.;
984 xc_draw_glyph_object(theinstance, passwidth);
986 extents->x_bearing = llx;
987 extents->y_bearing = -try;
988 extents->width = trx - llx;
989 extents->height = try - lly;
990 extents->x_advance = (chr->bbox.lowerleft.x + chr->bbox.width) / 40.;
991 extents->y_advance = 0.;
993 areawin->cr = old_cr;
994 return CAIRO_STATUS_SUCCESS;
997 /*----------------------------------------------------------------------*/
998 /* Function to translate unicode into a glyph index for the stroke font */
999 /*----------------------------------------------------------------------*/
1001 static cairo_status_t xc_user_font_glyph(cairo_scaled_font_t *scaled_font,
1002 unsigned long unicode, unsigned long *glyph_index)
1004 cairo_font_face_t *ff = cairo_scaled_font_get_font_face(scaled_font);
1005 size_t fontidx = (size_t) cairo_font_face_get_user_data(ff, &fontinfo_key);
1006 fontinfo *fi = &fonts[fontidx];
1007 unsigned long idx;
1009 /* search all glyphs in the utf8encoding. This is allowed to be slow, */
1010 /* as the glyph list will be buffered anyway */
1011 for (idx = 1; idx < 255; idx++) {
1012 const char *s = fi->utf8encoding[idx];
1013 int cidx = 0;
1015 /* Convert utf-8 to unicode */
1016 unsigned long uc = s[0];
1017 if (uc & 0x80) {
1018 unsigned long mask = 0x3f;
1019 while ((s[++cidx] & 0xc0) == 0x80) {
1020 uc = (uc << 6) | (s[cidx] & 0x3f);
1021 mask = (mask << 5) | 0x1f;
1023 uc &= mask;
1026 if (unicode == uc) {
1027 *glyph_index = idx;
1028 return CAIRO_STATUS_SUCCESS;
1031 /* This should not happen: replace unknown glyph with question mark */
1032 *glyph_index = '?';
1033 return CAIRO_STATUS_SUCCESS;
1036 /*---------------------------------------------------------------------*/
1037 /* find the corresponing cairo fontface for a given fontinfo structure */
1038 /*---------------------------------------------------------------------*/
1040 typedef struct {
1041 const char* postscript_name;
1042 const char* replacement_name;
1043 const char* foundry_name;
1044 } xc_font_replacement;
1046 static const xc_font_replacement replacement_fonts[] =
1048 /* First try to see if 'real' postscript fonts have been installed */
1049 {"ITC Avant Garde Gothic", "ITC Avant Garde Gothic", "adobe"},
1050 {"ITC Bookman", "ITC Bookman", "adobe"},
1051 {"Courier", "Courier", "adobe"},
1052 {"Helvetica", "Helvetica", "adobe"},
1053 {"Helvetica Narrow", "Helvetica Narrow", "adobe"},
1054 {"New Century Schoolbook", "New Century Schoolbook", "adobe"},
1055 {"Palatino", "Palatino", "adobe"},
1056 {"Symbol", "Symbol", "adobe"},
1057 {"Times", "Times", "adobe"},
1058 {"Times-Roman", "Times-Roman", "adobe"},
1059 {"ITC ZapfChangery", "ITC ZapfChangery", "adobe"},
1060 {"ITC ZapfDingbats", "ITC ZapfDingbats", "adobe"},
1061 /* Next try the URW postscript fonts (guaranteed to have same extents) */
1062 {"ITC Avant Garde Gothic", "URW Gothic L", "urw"},
1063 {"ITC Bookman", "URW Bookman L", "urw"},
1064 {"Courier", "Nimbus Mono L", "urw"},
1065 {"Helvetica", "Nimbus Sans L", "urw"},
1066 {"Helvetica Narrow", "Nimbus Sans L Condensed", "urw"},
1067 {"New Century Schoolbook", "Century Schoolbook L", "urw"},
1068 {"Palatino", "URW Palladio L", "urw"},
1069 {"Symbol", "Standard Symbols L", "urw"},
1070 {"Times", "Nimbus Roman No9 L", "urw"},
1071 {"Times-Roman", "Nimbus Roman No9 L", "urw"},
1072 {"ITC ZapfChangery", "URW Changery L", "urw"},
1073 {"ITC ZapfDingbats", "Dingbats", "urw"},
1074 /* No success, use the 'old' stroke fonts */
1075 {NULL, NULL, NULL}
1078 void xc_cairo_set_fontinfo(size_t fontidx)
1080 /* TODO: memory leak. font_face is created here. It should also be */
1081 /* destroyed again somewhere */
1082 fontinfo *fi = &fonts[fontidx];
1083 const char *family = fi->family;
1084 const xc_font_replacement *replace;
1085 int c;
1087 #ifdef CAIRO_HAS_FC_FONT
1088 int weight = FC_WEIGHT_NORMAL;
1089 int slant = FC_SLANT_ROMAN;
1091 fi->font_face = NULL;
1093 if (fi->flags & 1)
1094 weight = FC_WEIGHT_BOLD;
1096 if (fi->flags & 2) {
1097 if (!strcmp(family, "Helvetica"))
1098 slant = FC_SLANT_OBLIQUE;
1099 else
1100 slant = FC_SLANT_ITALIC;
1102 /* Try to find a proper postscript font */
1103 for (replace = replacement_fonts; replace->postscript_name; replace++) {
1104 if (!strcmp(replace->postscript_name, fi->family)) {
1105 FcPattern *matched;
1106 FcChar8 *matched_family, *matched_foundry;
1107 FcResult result;
1108 FcPattern *pattern = FcPatternBuild(NULL,
1109 FC_FAMILY, FcTypeString, replace->replacement_name,
1110 FC_WEIGHT, FcTypeInteger, weight,
1111 FC_SLANT, FcTypeInteger, slant,
1112 FC_FOUNDRY, FcTypeString, replace->foundry_name,
1113 NULL);
1114 FcConfigSubstitute(0, pattern, FcMatchPattern);
1115 FcDefaultSubstitute(pattern);
1116 matched = FcFontMatch(0, pattern, &result);
1117 /* Check if the matched font is actually the replacement font */
1118 FcPatternGetString(matched, FC_FAMILY, 0, &matched_family);
1119 FcPatternGetString(matched, FC_FOUNDRY, 0, &matched_foundry);
1120 if (!strcmp(matched_family, replace->replacement_name)
1121 && !strcmp(matched_foundry, replace->foundry_name))
1122 fi->font_face = cairo_ft_font_face_create_for_pattern(matched);
1123 FcPatternDestroy(matched);
1124 FcPatternDestroy(pattern);
1125 if (fi->font_face)
1126 break;
1129 #else /* CAIRO_HAS_FC_FONT */
1130 fi->font_face = NULL;
1131 #endif /* CAIRO_HAS_FC_FONT */
1133 /* Cache the dimensions and all glyphs of the font */
1134 if (fi->font_face) {
1135 int num_glyphs;
1136 cairo_glyph_t *glyphs = NULL;
1137 cairo_scaled_font_t *scaled_font;
1138 cairo_text_extents_t extents;
1140 cairo_save(areawin->cr);
1141 cairo_identity_matrix(areawin->cr);
1142 cairo_set_font_face(areawin->cr, fi->font_face);
1143 cairo_set_font_size(areawin->cr, 100.);
1144 scaled_font = cairo_get_scaled_font(areawin->cr);
1145 for (c = 1; c < 256; c++) { /* skip encoding at index 0 */
1146 cairo_scaled_font_text_to_glyphs(scaled_font, 0., 0.,
1147 fi->utf8encoding[c], -1, &glyphs, &num_glyphs,
1148 NULL, NULL, NULL);
1149 fi->glyph_index[c] = glyphs[0].index;
1150 cairo_scaled_font_glyph_extents(scaled_font, glyphs, 1, &extents);
1151 fi->glyph_top[c] = -extents.y_bearing * 40. / 100.;
1152 /* fi->glyph_bottom[c] = extents.height * 40. / 100. + fi->glyph_top[c]; */
1153 fi->glyph_bottom[c] = fi->glyph_top[c] - extents.height * 40. / 100.;
1154 fi->glyph_advance[c] = extents.x_advance * 40. / 100.;
1156 cairo_glyph_free(glyphs);
1157 cairo_restore(areawin->cr);
1159 else {
1160 /* No decent postscript font found. Backup using stroke fonts */
1161 fi->font_face = cairo_user_font_face_create();
1162 cairo_font_face_set_user_data(fi->font_face, &fontinfo_key,
1163 (void*) fontidx, (cairo_destroy_func_t) cairo_font_face_destroy);
1164 cairo_user_font_face_set_render_glyph_func(fi->font_face,
1165 xc_user_font_render);
1166 cairo_user_font_face_set_unicode_to_glyph_func(fi->font_face,
1167 xc_user_font_glyph);
1168 for (c = 0; c < 256; c++) {
1169 objectptr chr = fi->encoding[c];
1170 fi->glyph_index[c] = c;
1171 fi->glyph_top[c] = chr->bbox.lowerleft.y + chr->bbox.height;
1172 fi->glyph_bottom[c] = chr->bbox.lowerleft.y;
1173 fi->glyph_advance[c] = chr->bbox.lowerleft.x + chr->bbox.width;
1178 /*----------------------------------------------------------------------*/
1179 /* A light wrapper around cairo_surface_t, to a generalized xcImage */
1180 /*----------------------------------------------------------------------*/
1182 /* caching for cairo_surface_t */
1183 static xcImage *xcImagePixel_oldimg = NULL;
1184 static uint32_t *xcImagePixel_data;
1185 static int xcImagePixel_width;
1186 static int xcImagePixel_height;
1188 static inline void xcImageCheckCache(xcImage *img)
1190 if (img != xcImagePixel_oldimg) {
1191 xcImagePixel_oldimg = img;
1192 xcImagePixel_data = (uint32_t*) cairo_image_surface_get_data(img);
1193 xcImagePixel_width = cairo_image_surface_get_width(img);
1194 xcImagePixel_height = cairo_image_surface_get_height(img);
1198 xcImage *xcImageCreate(int width, int height)
1200 return cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
1203 void xcImageDestroy(xcImage *img)
1205 cairo_surface_destroy(img);
1208 int xcImageGetWidth(xcImage *img)
1210 xcImageCheckCache(img);
1211 return xcImagePixel_width;
1214 int xcImageGetHeight(xcImage *img)
1216 xcImageCheckCache(img);
1217 return xcImagePixel_height;
1220 void xcImagePutPixel(xcImage *img, int x, int y, u_char r, u_char g, u_char b)
1222 xcImageCheckCache(img);
1223 xcImagePixel_data[y * xcImagePixel_width + x] = (r << 16) | (g << 8) | b;
1226 void xcImageGetPixel(xcImage *img, int x, int y, u_char *r, u_char *g,
1227 u_char *b)
1229 uint32_t argb;
1230 xcImageCheckCache(img);
1231 argb = xcImagePixel_data[y * xcImagePixel_width + x];
1232 *r = argb >> 16;
1233 *g = argb >> 8;
1234 *b = argb;
1238 /*------------------------------------------------------------------------*/
1239 /* Ghostscript rendering function. In contrast to X11, direct calls to */
1240 /* ghostscriptapi are made */
1241 /*------------------------------------------------------------------------*/
1243 /*------------------------------------------------------------------------*/
1244 /* gsapi I/O redirection functions */
1245 /*------------------------------------------------------------------------*/
1247 #ifdef HAVE_GS
1248 static int GSDLLCALL gs_stdin_fn(void *caller_handle, char *buf, int len)
1250 UNUSED(caller_handle); UNUSED(buf); UNUSED(len);
1252 return 0; /* EOF */
1255 static int GSDLLCALL gs_stdout_fn(void *caller_handle, const char *str, int len)
1257 #ifndef GS_DEBUG
1258 UNUSED(caller_handle); UNUSED(str);
1259 #else
1260 fwrite(str, 1, len, stdout);
1261 fflush(stdout);
1262 #endif
1263 return len;
1266 static int GSDLLCALL gs_stderr_fn(void *caller_handle, const char *str, int len)
1268 #ifndef GS_DEBUG
1269 UNUSED(caller_handle); UNUSED(str);
1270 #else
1271 fwrite(str, 1, len, stderr);
1272 fflush(stderr);
1273 #endif
1274 return len;
1276 #endif /* HAVE_GS */
1278 /*------------------------------------------------------------------------*/
1279 /* gsapi displaycallback functions */
1280 /*------------------------------------------------------------------------*/
1282 #ifdef HAVE_GS
1283 static unsigned char *gs_pimage;
1284 static int gs_width, gs_height, gs_raster;
1286 static int gs_display_dummy(void)
1288 return 0;
1290 #endif /* HAVE_GS */
1292 #ifdef HAVE_GS
1293 static int gs_display_size(void *handle, void *device, int width, int height,
1294 int raster, unsigned int format, unsigned char *pimage)
1296 UNUSED(handle); UNUSED(device); UNUSED(format);
1298 gs_pimage = pimage;
1299 gs_width = width;
1300 gs_height = height;
1301 gs_raster = raster;
1302 return 0;
1304 #endif /* HAVE_GS */
1306 #ifdef HAVE_GS
1307 int gs_display_page(void *handle, void *device, int copies, int flush)
1309 cairo_surface_t *tbuf;
1310 cairo_t *bbuf_cr;
1311 UNUSED(handle); UNUSED(device); UNUSED(copies); UNUSED(flush);
1313 if (bbuf)
1314 cairo_surface_destroy(bbuf);
1315 /* Since cairo_image_surface_create_for_data assumes the buffer to be */
1316 /* valid until destruction, and ghostscript frees it immediately, */
1317 /* first use a temporary buffer. Then immediately copy the buffer */
1318 /* To the final location */
1319 tbuf = cairo_image_surface_create_for_data(gs_pimage, CAIRO_FORMAT_RGB24,
1320 gs_width, gs_height, gs_raster);
1321 bbuf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, gs_width, gs_height);
1322 bbuf_cr = cairo_create(bbuf);
1323 cairo_set_source_surface(bbuf_cr, tbuf, 0., 0.);
1324 cairo_paint(bbuf_cr);
1325 cairo_destroy(bbuf_cr);
1326 cairo_surface_destroy(tbuf);
1327 return 0;
1329 #endif /* HAVE_GS */
1331 #ifdef HAVE_GS
1332 display_callback gs_display = {
1333 sizeof(display_callback),
1334 DISPLAY_VERSION_MAJOR,
1335 DISPLAY_VERSION_MINOR,
1336 (int (*)(void*, void*)) gs_display_dummy, /* display_open */
1337 (int (*)(void*, void*)) gs_display_dummy, /* display_preclose */
1338 (int (*)(void*, void*)) gs_display_dummy, /* display_close */
1339 (int (*)(void*, void*, int, int, int, unsigned int)) gs_display_dummy,
1340 /* display_presize */
1341 gs_display_size,
1342 (int (*)(void*, void*)) gs_display_dummy, /* display_sync */
1343 gs_display_page,
1344 NULL, /* display_update */
1345 NULL, /* display_memalloc */
1346 NULL, /* display_memfree */
1347 NULL /* display_separation */
1349 #endif /* HAVE_GS */
1351 /*------------------------------------------------------*/
1352 /* write scale and position to ghostscript */
1353 /* and tell ghostscript to run the requested file */
1354 /*------------------------------------------------------*/
1356 #ifdef HAVE_GS
1357 const char *gs_argv[] = {
1358 "-dQUIET", /* Suppress startup messages */
1359 "-dNOPAUSE", /* Disable pause at end of page */
1360 "-dBATCH", /* Exit when all files done */
1361 "-sDEVICE=display",
1362 "-sDisplayHandle=0",
1363 "-r75", /* Display resolution */
1364 "-dGraphicsAlphaBits=4", /* Graphics anti-aliasing */
1365 "-dTextAlphaBits=4" /* Text anti-aliasing */
1367 #endif /* HAVE_GS */
1369 void write_scale_position_and_run_gs(float norm, float xpos, float ypos,
1370 const char *bgfile)
1372 #ifndef HAVE_GS
1373 UNUSED(norm); UNUSED(xpos); UNUSED(ypos); UNUSED(bgfile);
1374 #endif
1375 #ifdef HAVE_GS
1376 int i, code, exit_code;
1377 void *instance;
1378 char gs_cmd[256];
1379 char display_format[] = "-dDisplayFormat=........";
1380 char pixmap_size[] = "-g........x........";
1381 int argc = sizeof(gs_argv) / sizeof(gs_argv[0]);
1382 const char **argv = (const char**) malloc((argc + 2) * sizeof(const char*));
1384 for (i = 0; i < argc; i++)
1385 argv[i] = gs_argv[i];
1386 argv[argc++] = display_format;
1387 argv[argc++] = pixmap_size;
1389 sprintf(gs_cmd,
1390 " /GSobj save def"
1391 " /setpagedevice {pop} def"
1392 " gsave"
1393 " %3.2f %3.2f translate"
1394 " %3.2f %3.2f scale"
1395 " (%s) run"
1396 " GSobj restore"
1397 " grestore",
1398 xpos, ypos, norm, norm, bgfile);
1400 sprintf(display_format, "-dDisplayFormat=%d", DISPLAY_COLORS_RGB
1401 | DISPLAY_UNUSED_LAST | DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN
1402 | DISPLAY_TOPFIRST | DISPLAY_ROW_ALIGN_DEFAULT);
1403 sprintf(pixmap_size, "-g%dx%d", areawin->width, areawin->height);
1405 XDefineCursor(dpy, areawin->window, WAITFOR);
1407 if ((code = gsapi_new_instance(&instance, NULL)) == 0) {
1408 gsapi_set_stdio(instance, gs_stdin_fn, gs_stdout_fn, gs_stderr_fn);
1409 gsapi_set_display_callback(instance, &gs_display);
1410 if (!(code = gsapi_init_with_args(instance, argc, (char**) argv)))
1411 gsapi_run_string(instance, gs_cmd, 0, &exit_code);
1412 gsapi_exit(instance);
1413 gsapi_delete_instance(instance);
1415 free(argv);
1416 #ifdef GS_DEBUG
1417 fprintf(stdout, "Xcircuit: ghostscript done\n");
1418 #endif
1420 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
1421 areawin->lastbackground = xobjs.pagelist[areawin->page]->background.name;
1422 drawarea(areawin->area, NULL, NULL);
1423 #endif /* HAVE_GS */
1424 gs_state = GS_READY;
1427 #else /* HAVE_CAIRO */
1428 typedef int no_empty_translation_unit_warning;
1429 #endif /* HAVE_CAIRO */