1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * diapsrenderer.c -- implements the base class for Postscript rendering
5  *   It is mostly refactoring of render_eps.c (some stuff not from the
6  *   latest version but from 1.24) before PS rendering became multi-pass
7  *   and text rendering became (necessary) complicated.
8  * Refatoring: Copyright (C) 2002 Hans Breuer
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23  */
24 
25 #include <config.h>
26 
27 #include <string.h>
28 #include <time.h>
29 
30 #include "diapsrenderer.h"
31 #include "message.h"
32 #include "dia_image.h"
33 #include "font.h"
34 #include "textline.h"
35 
36 #define DTOSTR_BUF_SIZE G_ASCII_DTOSTR_BUF_SIZE
37 #define psrenderer_dtostr(buf,d) \
38 	g_ascii_formatd(buf, sizeof(buf), "%f", d)
39 
40 /* Returns TRUE if this file is an encapsulated postscript file
41  * (including e.g. epsi).
42  */
renderer_is_eps(DiaPsRenderer * renderer)43 static gboolean renderer_is_eps(DiaPsRenderer *renderer) {
44   return renderer->pstype == PSTYPE_EPS ||
45     renderer->pstype == PSTYPE_EPSI;
46 }
47 
48 /** Returns TRUE if this file is an EPSI file */
renderer_is_epsi(DiaPsRenderer * renderer)49 static gboolean renderer_is_epsi(DiaPsRenderer *renderer) {
50   return renderer->pstype == PSTYPE_EPSI;
51 }
52 
53 void
lazy_setcolor(DiaPsRenderer * renderer,Color * color)54 lazy_setcolor(DiaPsRenderer *renderer,
55               Color *color)
56 {
57   gchar r_buf[DTOSTR_BUF_SIZE];
58   gchar g_buf[DTOSTR_BUF_SIZE];
59   gchar b_buf[DTOSTR_BUF_SIZE];
60 
61   if (!color_equals(color, &(renderer->lcolor))) {
62     renderer->lcolor = *color;
63     fprintf(renderer->file, "%s %s %s srgb\n",
64             psrenderer_dtostr(r_buf, (gdouble) color->red),
65             psrenderer_dtostr(g_buf, (gdouble) color->green),
66             psrenderer_dtostr(b_buf, (gdouble) color->blue) );
67   }
68 }
69 
70 static void
begin_render(DiaRenderer * self)71 begin_render(DiaRenderer *self)
72 {
73   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
74   time_t time_now;
75 
76   g_assert (renderer->file != NULL);
77 
78   time_now  = time(NULL);
79 
80   if (renderer_is_eps(renderer))
81     fprintf(renderer->file,
82             "%%!PS-Adobe-2.0 EPSF-2.0\n");
83   else
84     fprintf(renderer->file,
85             "%%!PS-Adobe-2.0\n");
86   fprintf(renderer->file,
87           "%%%%Title: %s\n"
88           "%%%%Creator: Dia v%s\n"
89           "%%%%CreationDate: %s"
90           "%%%%For: %s\n"
91           "%%%%Orientation: %s\n",
92           renderer->title ? renderer->title : "(NULL)" ,
93           VERSION,
94           ctime(&time_now),
95           g_get_user_name(),
96           renderer->is_portrait ? "Portrait" : "Landscape");
97 
98   if (renderer_is_epsi(renderer)) {
99     g_assert(!"Preview image not implmented");
100     /* but it *may* belong here ... */
101   }
102   if (renderer_is_eps(renderer))
103     fprintf(renderer->file,
104             "%%%%Magnification: 1.0000\n"
105 	  "%%%%BoundingBox: 0 0 %d %d\n",
106             (int) ceil(  (renderer->extent.right - renderer->extent.left)
107                        * renderer->scale),
108             (int) ceil(  (renderer->extent.bottom - renderer->extent.top)
109                        * renderer->scale) );
110 
111   else
112     fprintf(renderer->file,
113 	  "%%%%DocumentPaperSizes: %s\n",
114             renderer->paper ? renderer->paper : "(NULL)");
115 
116   fprintf(renderer->file,
117           "%%%%BeginSetup\n");
118   fprintf(renderer->file,
119           "%%%%EndSetup\n"
120           "%%%%EndComments\n");
121 
122   DIA_PS_RENDERER_GET_CLASS(self)->begin_prolog(renderer);
123   /* get our font definitions */
124   DIA_PS_RENDERER_GET_CLASS(self)->dump_fonts(renderer);
125   /* done it */
126   DIA_PS_RENDERER_GET_CLASS(self)->end_prolog(renderer);
127 }
128 
129 static void
end_render(DiaRenderer * self)130 end_render(DiaRenderer *self)
131 {
132   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
133 
134   if (renderer_is_eps(renderer))
135     fprintf(renderer->file, "showpage\n");
136 
137   if (self->font != NULL) {
138     dia_font_unref(self->font);
139     self->font = NULL;
140   }
141 }
142 
143 static void
set_linewidth(DiaRenderer * self,real linewidth)144 set_linewidth(DiaRenderer *self, real linewidth)
145 {  /* 0 == hairline **/
146   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
147   gchar lw_buf[DTOSTR_BUF_SIZE];
148 
149   /* Adobe's advice:  Set to very small but fixed size, to avoid changes
150    * due to different resolution output. */
151   /* .01 cm becomes <5 dots on 1200 DPI */
152   if (linewidth == 0.0) linewidth=.01;
153   fprintf(renderer->file, "%s slw\n",
154 	  psrenderer_dtostr(lw_buf, (gdouble) linewidth) );
155 }
156 
157 static void
set_linecaps(DiaRenderer * self,LineCaps mode)158 set_linecaps(DiaRenderer *self, LineCaps mode)
159 {
160   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
161   int ps_mode;
162 
163   switch(mode) {
164   case LINECAPS_BUTT:
165     ps_mode = 0;
166     break;
167   case LINECAPS_ROUND:
168     ps_mode = 1;
169     break;
170   case LINECAPS_PROJECTING:
171     ps_mode = 2;
172     break;
173   default:
174     ps_mode = 0;
175   }
176 
177   fprintf(renderer->file, "%d slc\n", ps_mode);
178 }
179 
180 static void
set_linejoin(DiaRenderer * self,LineJoin mode)181 set_linejoin(DiaRenderer *self, LineJoin mode)
182 {
183   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
184   int ps_mode;
185 
186   switch(mode) {
187   case LINEJOIN_MITER:
188     ps_mode = 0;
189     break;
190   case LINEJOIN_ROUND:
191     ps_mode = 1;
192     break;
193   case LINEJOIN_BEVEL:
194     ps_mode = 2;
195     break;
196   default:
197     ps_mode = 0;
198   }
199 
200   fprintf(renderer->file, "%d slj\n", ps_mode);
201 }
202 
203 static void
set_linestyle(DiaRenderer * self,LineStyle mode)204 set_linestyle(DiaRenderer *self, LineStyle mode)
205 {
206   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
207   real hole_width;
208   gchar dashl_buf[DTOSTR_BUF_SIZE];
209   gchar dotl_buf[DTOSTR_BUF_SIZE];
210   gchar holew_buf[DTOSTR_BUF_SIZE];
211 
212   renderer->saved_line_style = mode;
213 
214   switch(mode) {
215   case LINESTYLE_SOLID:
216     fprintf(renderer->file, "[] 0 sd\n");
217     break;
218   case LINESTYLE_DASHED:
219     fprintf(renderer->file, "[%s] 0 sd\n",
220 	    psrenderer_dtostr(dashl_buf, renderer->dash_length) );
221     break;
222   case LINESTYLE_DASH_DOT:
223     hole_width = (renderer->dash_length - renderer->dot_length) / 2.0;
224     psrenderer_dtostr(holew_buf, hole_width);
225     psrenderer_dtostr(dashl_buf, renderer->dash_length);
226     psrenderer_dtostr(dotl_buf, renderer->dot_length);
227     fprintf(renderer->file, "[%s %s %s %s] 0 sd\n",
228 	    dashl_buf,
229 	    holew_buf,
230 	    dotl_buf,
231 	    holew_buf );
232     break;
233   case LINESTYLE_DASH_DOT_DOT:
234     hole_width = (renderer->dash_length - 2.0*renderer->dot_length) / 3.0;
235     psrenderer_dtostr(holew_buf, hole_width);
236     psrenderer_dtostr(dashl_buf, renderer->dash_length);
237     psrenderer_dtostr(dotl_buf, renderer->dot_length);
238     fprintf(renderer->file, "[%s %s %s %s %s %s] 0 sd\n",
239 	    dashl_buf,
240 	    holew_buf,
241 	    dotl_buf,
242 	    holew_buf,
243 	    dotl_buf,
244 	    holew_buf );
245     break;
246   case LINESTYLE_DOTTED:
247     fprintf(renderer->file, "[%s] 0 sd\n",
248 	    psrenderer_dtostr(dotl_buf, renderer->dot_length) );
249     break;
250   }
251 }
252 
253 static void
set_dashlength(DiaRenderer * self,real length)254 set_dashlength(DiaRenderer *self, real length)
255 {  /* dot = 20% of len */
256   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
257 
258   if (length<0.001)
259     length = 0.001;
260 
261   renderer->dash_length = length;
262   renderer->dot_length = length*0.2;
263 
264   set_linestyle(self, renderer->saved_line_style);
265 }
266 
267 static void
set_fillstyle(DiaRenderer * self,FillStyle mode)268 set_fillstyle(DiaRenderer *self, FillStyle mode)
269 {
270   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
271 
272   switch(mode) {
273   case FILLSTYLE_SOLID:
274     break;
275   default:
276     message_error("%s : Unsupported fill mode specified!\n",
277                   G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
278   }
279 }
280 
281 static void
set_font(DiaRenderer * self,DiaFont * font,real height)282 set_font(DiaRenderer *self, DiaFont *font, real height)
283 {
284   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
285   gchar h_buf[DTOSTR_BUF_SIZE];
286 
287   if (font != self->font || height != self->font_height) {
288     DiaFont *old_font;
289 
290     fprintf(renderer->file, "/%s-latin1 ff %s scf sf\n",
291 	    dia_font_get_psfontname(font),
292 	    psrenderer_dtostr(h_buf, (gdouble) height*0.7) );
293     old_font = self->font;
294     self->font = font;
295     dia_font_ref(self->font);
296     if (old_font != NULL) {
297       dia_font_unref(old_font);
298     }
299     self->font_height = height;
300   }
301 }
302 
303 static void
draw_line(DiaRenderer * self,Point * start,Point * end,Color * line_color)304 draw_line(DiaRenderer *self,
305 	  Point *start, Point *end,
306 	  Color *line_color)
307 {
308   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
309   gchar sx_buf[DTOSTR_BUF_SIZE];
310   gchar sy_buf[DTOSTR_BUF_SIZE];
311   gchar ex_buf[DTOSTR_BUF_SIZE];
312   gchar ey_buf[DTOSTR_BUF_SIZE];
313 
314   lazy_setcolor(renderer,line_color);
315 
316   fprintf(renderer->file, "n %s %s m %s %s l s\n",
317 	  psrenderer_dtostr(sx_buf, start->x),
318 	  psrenderer_dtostr(sy_buf, start->y),
319 	  psrenderer_dtostr(ex_buf, end->x),
320 	  psrenderer_dtostr(ey_buf, end->y) );
321 }
322 
323 static void
draw_polyline(DiaRenderer * self,Point * points,int num_points,Color * line_color)324 draw_polyline(DiaRenderer *self,
325 	      Point *points, int num_points,
326 	      Color *line_color)
327 {
328   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
329   int i;
330   gchar px_buf[DTOSTR_BUF_SIZE];
331   gchar py_buf[DTOSTR_BUF_SIZE];
332 
333   lazy_setcolor(renderer,line_color);
334 
335   fprintf(renderer->file, "n %s %s m ",
336 	  psrenderer_dtostr(px_buf, points[0].x),
337 	  psrenderer_dtostr(py_buf, points[0].y) );
338 
339   for (i=1;i<num_points;i++) {
340     fprintf(renderer->file, "%s %s l ",
341 	    psrenderer_dtostr(px_buf, points[i].x),
342 	    psrenderer_dtostr(py_buf, points[i].y) );
343   }
344 
345   fprintf(renderer->file, "s\n");
346 }
347 
348 static void
psrenderer_polygon(DiaPsRenderer * renderer,Point * points,gint num_points,Color * line_color,gboolean filled)349 psrenderer_polygon(DiaPsRenderer *renderer,
350 		   Point *points,
351 		   gint num_points,
352 		   Color *line_color,
353 		   gboolean filled)
354 {
355   gint i;
356   gchar px_buf[DTOSTR_BUF_SIZE];
357   gchar py_buf[DTOSTR_BUF_SIZE];
358 
359   lazy_setcolor(renderer, line_color);
360 
361   fprintf(renderer->file, "n %s %s m ",
362 	  psrenderer_dtostr(px_buf, points[0].x),
363 	  psrenderer_dtostr(py_buf, points[0].y) );
364 
365 
366   for (i=1;i<num_points;i++) {
367     fprintf(renderer->file, "%s %s l ",
368 	    psrenderer_dtostr(px_buf, points[i].x),
369 	    psrenderer_dtostr(py_buf, points[i].y) );
370   }
371   if (filled)
372     fprintf(renderer->file, "ef\n");
373   else
374     fprintf(renderer->file, "cp s\n");
375 }
376 
377 static void
draw_polygon(DiaRenderer * self,Point * points,int num_points,Color * line_color)378 draw_polygon(DiaRenderer *self,
379 	      Point *points, int num_points,
380 	      Color *line_color)
381 {
382   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
383   psrenderer_polygon(renderer, points, num_points, line_color, FALSE);
384 }
385 
386 static void
fill_polygon(DiaRenderer * self,Point * points,int num_points,Color * fill_color)387 fill_polygon(DiaRenderer *self,
388 	      Point *points, int num_points,
389 	      Color *fill_color)
390 {
391   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
392   psrenderer_polygon(renderer, points, num_points, fill_color, TRUE);
393 }
394 
395 static void
psrenderer_rect(DiaPsRenderer * renderer,Point * ul_corner,Point * lr_corner,Color * color,gboolean filled)396 psrenderer_rect(DiaPsRenderer *renderer,
397 		Point *ul_corner,
398 		Point *lr_corner,
399 		Color *color,
400 		gboolean filled)
401 {
402   gchar ulx_buf[DTOSTR_BUF_SIZE];
403   gchar uly_buf[DTOSTR_BUF_SIZE];
404   gchar lrx_buf[DTOSTR_BUF_SIZE];
405   gchar lry_buf[DTOSTR_BUF_SIZE];
406 
407   lazy_setcolor(renderer,color);
408 
409   psrenderer_dtostr(ulx_buf, (gdouble) ul_corner->x);
410   psrenderer_dtostr(uly_buf, (gdouble) ul_corner->y);
411   psrenderer_dtostr(lrx_buf, (gdouble) lr_corner->x);
412   psrenderer_dtostr(lry_buf, (gdouble) lr_corner->y);
413 
414   fprintf(renderer->file, "n %s %s m %s %s l %s %s l %s %s l %s\n",
415 	  ulx_buf, uly_buf,
416 	  ulx_buf, lry_buf,
417 	  lrx_buf, lry_buf,
418 	  lrx_buf, uly_buf,
419 	  filled ? "f" : "cp s" );
420 }
421 
422 static void
draw_rect(DiaRenderer * self,Point * ul_corner,Point * lr_corner,Color * color)423 draw_rect(DiaRenderer *self,
424 	  Point *ul_corner, Point *lr_corner,
425 	  Color *color)
426 {
427   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
428   psrenderer_rect(renderer, ul_corner, lr_corner, color, FALSE);
429 }
430 
431 static void
fill_rect(DiaRenderer * self,Point * ul_corner,Point * lr_corner,Color * color)432 fill_rect(DiaRenderer *self,
433 	  Point *ul_corner, Point *lr_corner,
434 	  Color *color)
435 {
436   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
437   psrenderer_rect(renderer, ul_corner, lr_corner, color, TRUE);
438 }
439 
440 static void
psrenderer_arc(DiaPsRenderer * renderer,Point * center,real width,real height,real angle1,real angle2,Color * color,gboolean filled)441 psrenderer_arc(DiaPsRenderer *renderer,
442 	       Point *center,
443 	       real width, real height,
444 	       real angle1, real angle2,
445 	       Color *color,
446 	       gboolean filled)
447 {
448   gchar cx_buf[DTOSTR_BUF_SIZE];
449   gchar cy_buf[DTOSTR_BUF_SIZE];
450   gchar a1_buf[DTOSTR_BUF_SIZE];
451   gchar a2_buf[DTOSTR_BUF_SIZE];
452   gchar w_buf[DTOSTR_BUF_SIZE];
453   gchar h_buf[DTOSTR_BUF_SIZE];
454 
455   lazy_setcolor(renderer, color);
456 
457   psrenderer_dtostr(cx_buf, (gdouble) center->x);
458   psrenderer_dtostr(cy_buf, (gdouble) center->y);
459   psrenderer_dtostr(a1_buf, (gdouble) 360.0 - angle1);
460   psrenderer_dtostr(a2_buf, (gdouble) 360.0 - angle2);
461   psrenderer_dtostr(w_buf, (gdouble) width / 2.0);
462   psrenderer_dtostr(h_buf, (gdouble) height / 2.0);
463 
464   fprintf(renderer->file, "n ");
465 
466   if (filled)
467     fprintf(renderer->file, "%s %s m ", cx_buf, cy_buf);
468 
469   fprintf(renderer->file, "%s %s %s %s %s %s ellipse %s\n",
470 	  cx_buf, cy_buf, w_buf, h_buf, a2_buf, a1_buf,
471 	  filled ? "f" : "s" );
472 }
473 
474 
475 static void
draw_arc(DiaRenderer * self,Point * center,real width,real height,real angle1,real angle2,Color * color)476 draw_arc(DiaRenderer *self,
477 	 Point *center,
478 	 real width, real height,
479 	 real angle1, real angle2,
480 	 Color *color)
481 {
482   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
483   psrenderer_arc(renderer, center, width, height, angle1, angle2, color, FALSE);
484 }
485 
486 static void
fill_arc(DiaRenderer * self,Point * center,real width,real height,real angle1,real angle2,Color * color)487 fill_arc(DiaRenderer *self,
488 	 Point *center,
489 	 real width, real height,
490 	 real angle1, real angle2,
491 	 Color *color)
492 {
493   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
494   psrenderer_arc(renderer, center, width, height, angle1, angle2, color, TRUE);
495 }
496 
497 static void
psrenderer_ellipse(DiaPsRenderer * renderer,Point * center,real width,real height,Color * color,gboolean filled)498 psrenderer_ellipse(DiaPsRenderer *renderer,
499 		   Point *center,
500 		   real width, real height,
501 		   Color *color,
502 		   gboolean filled)
503 {
504   gchar cx_buf[DTOSTR_BUF_SIZE];
505   gchar cy_buf[DTOSTR_BUF_SIZE];
506   gchar w_buf[DTOSTR_BUF_SIZE];
507   gchar h_buf[DTOSTR_BUF_SIZE];
508 
509   lazy_setcolor(renderer,color);
510 
511   fprintf(renderer->file, "n %s %s %s %s 0 360 ellipse %s\n",
512 	  psrenderer_dtostr(cx_buf, (gdouble) center->x),
513 	  psrenderer_dtostr(cy_buf, (gdouble) center->y),
514 	  psrenderer_dtostr(w_buf, (gdouble) width / 2.0),
515 	  psrenderer_dtostr(h_buf, (gdouble) height / 2.0),
516 	  filled ? "f" : "cp s" );
517 }
518 
519 static void
draw_ellipse(DiaRenderer * self,Point * center,real width,real height,Color * color)520 draw_ellipse(DiaRenderer *self,
521 	     Point *center,
522 	     real width, real height,
523 	     Color *color)
524 {
525   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
526   psrenderer_ellipse(renderer, center, width, height, color, FALSE);
527 }
528 
529 static void
fill_ellipse(DiaRenderer * self,Point * center,real width,real height,Color * color)530 fill_ellipse(DiaRenderer *self,
531 	     Point *center,
532 	     real width, real height,
533 	     Color *color)
534 {
535   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
536   psrenderer_ellipse(renderer, center, width, height, color, TRUE);
537 }
538 
539 static void
psrenderer_bezier(DiaPsRenderer * renderer,BezPoint * points,gint numpoints,Color * color,gboolean filled)540 psrenderer_bezier(DiaPsRenderer *renderer,
541 		  BezPoint *points,
542 		  gint numpoints,
543 		  Color *color,
544 		  gboolean filled)
545 {
546   gint  i;
547   gchar p1x_buf[DTOSTR_BUF_SIZE];
548   gchar p1y_buf[DTOSTR_BUF_SIZE];
549   gchar p2x_buf[DTOSTR_BUF_SIZE];
550   gchar p2y_buf[DTOSTR_BUF_SIZE];
551   gchar p3x_buf[DTOSTR_BUF_SIZE];
552   gchar p3y_buf[DTOSTR_BUF_SIZE];
553 
554   lazy_setcolor(renderer,color);
555 
556   if (points[0].type != BEZ_MOVE_TO)
557     g_warning("first BezPoint must be a BEZ_MOVE_TO");
558 
559   fprintf(renderer->file, "n %s %s m",
560 	  psrenderer_dtostr(p1x_buf, (gdouble) points[0].p1.x),
561 	  psrenderer_dtostr(p1y_buf, (gdouble) points[0].p1.y) );
562 
563   for (i = 1; i < numpoints; i++)
564     switch (points[i].type) {
565     case BEZ_MOVE_TO:
566       g_warning("only first BezPoint can be a BEZ_MOVE_TO");
567       break;
568     case BEZ_LINE_TO:
569       fprintf(renderer->file, " %s %s l",
570 	      psrenderer_dtostr(p1x_buf, (gdouble) points[i].p1.x),
571 	      psrenderer_dtostr(p1y_buf, (gdouble) points[i].p1.y) );
572       break;
573     case BEZ_CURVE_TO:
574       fprintf(renderer->file, " %s %s %s %s %s %s c",
575 	      psrenderer_dtostr(p1x_buf, (gdouble) points[i].p1.x),
576 	      psrenderer_dtostr(p1y_buf, (gdouble) points[i].p1.y),
577 	      psrenderer_dtostr(p2x_buf, (gdouble) points[i].p2.x),
578 	      psrenderer_dtostr(p2y_buf, (gdouble) points[i].p2.y),
579 	      psrenderer_dtostr(p3x_buf, (gdouble) points[i].p3.x),
580 	      psrenderer_dtostr(p3y_buf, (gdouble) points[i].p3.y) );
581       break;
582     }
583 
584   if (filled)
585     fprintf(renderer->file, " ef\n");
586   else
587     fprintf(renderer->file, " s\n");
588 }
589 
590 static void
draw_bezier(DiaRenderer * self,BezPoint * points,int numpoints,Color * color)591 draw_bezier(DiaRenderer *self,
592 	    BezPoint *points,
593 	    int numpoints, /* numpoints = 4+3*n, n=>0 */
594 	    Color *color)
595 {
596   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
597   psrenderer_bezier(renderer, points, numpoints, color, FALSE);
598 }
599 
600 static void
fill_bezier(DiaRenderer * self,BezPoint * points,int numpoints,Color * color)601 fill_bezier(DiaRenderer *self,
602 	    BezPoint *points, /* Last point must be same as first point */
603 	    int numpoints,
604 	    Color *color)
605 {
606   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
607   psrenderer_bezier(renderer, points, numpoints, color, TRUE);
608 }
609 
610 static char*
ps_convert_string(const char * text)611 ps_convert_string(const char *text)
612 {
613   char *buffer;
614   gchar *localestr;
615   const gchar *str;
616   gint len;
617   GError * error = NULL;
618 
619   localestr = g_convert(text, -1, "LATIN1", "UTF-8", NULL, NULL, &error);
620 
621   if (localestr == NULL) {
622     message_error("Can't convert string %s: %s\n", text, error->message);
623     localestr = g_strdup(text);
624   }
625 
626   /* Escape all '(' and ')':  */
627   buffer = g_malloc(2*strlen(localestr)+1);
628   *buffer = 0;
629   str = localestr;
630   while (*str != 0) {
631     len = strcspn(str,"()\\");
632     strncat(buffer, str, len);
633     str += len;
634     if (*str != 0) {
635       strcat(buffer,"\\");
636       strncat(buffer, str, 1);
637       str++;
638     }
639   }
640   g_free(localestr);
641   return buffer;
642 }
643 
644 static void
put_text_alignment(DiaPsRenderer * renderer,Alignment alignment,Point * pos)645 put_text_alignment (DiaPsRenderer *renderer,
646 		    Alignment alignment,
647 		    Point *pos)
648 {
649   gchar px_buf[DTOSTR_BUF_SIZE];
650   gchar py_buf[DTOSTR_BUF_SIZE];
651 
652   switch (alignment) {
653   case ALIGN_LEFT:
654     fprintf(renderer->file, "%s %s m\n",
655 	    psrenderer_dtostr(px_buf, pos->x),
656 	    psrenderer_dtostr(py_buf, pos->y) );
657     break;
658   case ALIGN_CENTER:
659     fprintf(renderer->file, "dup sw 2 div %s ex sub %s m\n",
660 	    psrenderer_dtostr(px_buf, pos->x),
661 	    psrenderer_dtostr(py_buf, pos->y) );
662     break;
663   case ALIGN_RIGHT:
664     fprintf(renderer->file, "dup sw %s ex sub %s m\n",
665 	    psrenderer_dtostr(px_buf, pos->x),
666 	    psrenderer_dtostr(py_buf, pos->y) );
667     break;
668   }
669 }
670 
671 static void
draw_string(DiaRenderer * self,const char * text,Point * pos,Alignment alignment,Color * color)672 draw_string(DiaRenderer *self,
673 	    const char *text,
674 	    Point *pos, Alignment alignment,
675 	    Color *color)
676 {
677   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
678   Point pos_adj;
679   gchar *buffer;
680 
681   if (1 > strlen(text))
682     return;
683 
684   lazy_setcolor(renderer,color);
685 
686   buffer = ps_convert_string(text);
687 
688   fprintf(renderer->file, "(%s) ", buffer);
689   g_free(buffer);
690 
691   pos_adj.x = pos->x;
692   pos_adj.y = pos->y - dia_font_descent("", self->font, self->font_height);
693   put_text_alignment (renderer, alignment, &pos_adj);
694 
695   fprintf(renderer->file, " gs 1 -1 sc sh gr\n");
696 }
697 
698 static void
draw_image(DiaRenderer * self,Point * point,real width,real height,DiaImage * image)699 draw_image(DiaRenderer *self,
700 	   Point *point,
701 	   real width, real height,
702 	   DiaImage *image)
703 {
704   DiaPsRenderer *renderer = DIA_PS_RENDERER(self);
705   int img_width, img_height, img_rowstride;
706   int x, y;
707   real ratio;
708   guint8 *rgb_data;
709   guint8 *mask_data;
710   gchar d1_buf[DTOSTR_BUF_SIZE];
711   gchar d2_buf[DTOSTR_BUF_SIZE];
712 
713   img_width = dia_image_width(image);
714   img_rowstride = dia_image_rowstride(image);
715   img_height = dia_image_height(image);
716 
717   rgb_data = dia_image_rgb_data(image);
718   mask_data = dia_image_mask_data(image);
719 
720   ratio = height/width;
721 
722   fprintf(renderer->file, "gs\n");
723 
724   /* color output only */
725   fprintf(renderer->file, "/pix %i string def\n", img_width * 3);
726   fprintf(renderer->file, "%i %i 8\n", img_width, img_height);
727   fprintf(renderer->file, "%s %s tr\n",
728 			   psrenderer_dtostr(d1_buf, point->x),
729 			   psrenderer_dtostr(d2_buf, point->y) );
730   fprintf(renderer->file, "%s %s sc\n",
731 			   psrenderer_dtostr(d1_buf, width),
732 			   psrenderer_dtostr(d2_buf, height) );
733   fprintf(renderer->file, "[%i 0 0 %i 0 0]\n", img_width, img_height);
734 
735   fprintf(renderer->file, "{currentfile pix readhexstring pop}\n");
736   fprintf(renderer->file, "false 3 colorimage\n");
737   fprintf(renderer->file, "\n");
738 
739   if (mask_data) {
740     for (y = 0; y < img_height; y++) {
741       for (x = 0; x < img_width; x++) {
742 	int i = y*img_rowstride+x*3;
743 	int m = y*img_width+x;
744         fprintf(renderer->file, "%02x", 255-(mask_data[m]*(255-rgb_data[i])/255));
745         fprintf(renderer->file, "%02x", 255-(mask_data[m]*(255-rgb_data[i+1])/255));
746         fprintf(renderer->file, "%02x", 255-(mask_data[m]*(255-rgb_data[i+2])/255));
747       }
748       fprintf(renderer->file, "\n");
749     }
750   } else {
751     for (y = 0; y < img_height; y++) {
752       for (x = 0; x < img_width; x++) {
753 	int i = y*img_rowstride+x*3;
754         fprintf(renderer->file, "%02x", (int)(rgb_data[i]));
755         fprintf(renderer->file, "%02x", (int)(rgb_data[i+1]));
756         fprintf(renderer->file, "%02x", (int)(rgb_data[i+2]));
757       }
758       fprintf(renderer->file, "\n");
759     }
760   }
761   fprintf(renderer->file, "gr\n");
762   fprintf(renderer->file, "\n");
763 
764   g_free(rgb_data);
765   g_free(mask_data);
766 }
767 
768 static void
begin_prolog(DiaPsRenderer * renderer)769 begin_prolog (DiaPsRenderer *renderer)
770 {
771   g_assert(renderer->file != NULL);
772 
773   fprintf(renderer->file, "%%%%BeginProlog\n");
774   fprintf(renderer->file,
775 	  "[ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
776 	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
777 	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
778 	  "/.notdef /.notdef /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n"
779 	  "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one\n"
780 	  "/two /three /four /five /six /seven /eight /nine /colon /semicolon\n"
781 	  "/less /equal /greater /question /at /A /B /C /D /E\n"
782 	  "/F /G /H /I /J /K /L /M /N /O\n"
783 	  "/P /Q /R /S /T /U /V /W /X /Y\n"
784 	  "/Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c\n"
785 	  "/d /e /f /g /h /i /j /k /l /m\n"
786 	  "/n /o /p /q /r /s /t /u /v /w\n"
787 	  "/x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef\n"
788 	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
789 	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
790 	  "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
791 	  "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright\n"
792 	  "/ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior\n"
793 	  "/acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf\n"
794 	  "/threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
795 	  "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde\n"
796 	  "/Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex\n"
797 	  "/Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring\n"
798 	  "/ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
799 	  "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave\n"
800 	  "/uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] /isolatin1encoding exch def\n");
801 
802   fprintf(renderer->file,
803 	  "/cp {closepath} bind def\n"
804 	  "/c {curveto} bind def\n"
805 	  "/f {fill} bind def\n"
806 	  "/a {arc} bind def\n"
807 	  "/ef {eofill} bind def\n"
808 	  "/ex {exch} bind def\n"
809 	  "/gr {grestore} bind def\n"
810 	  "/gs {gsave} bind def\n"
811 	  "/sa {save} bind def\n"
812 	  "/rs {restore} bind def\n"
813 	  "/l {lineto} bind def\n"
814 	  "/m {moveto} bind def\n"
815 	  "/rm {rmoveto} bind def\n"
816 	  "/n {newpath} bind def\n"
817 	  "/s {stroke} bind def\n"
818 	  "/sh {show} bind def\n"
819 	  "/slc {setlinecap} bind def\n"
820 	  "/slj {setlinejoin} bind def\n"
821 	  "/slw {setlinewidth} bind def\n"
822 	  "/srgb {setrgbcolor} bind def\n"
823 	  "/rot {rotate} bind def\n"
824 	  "/sc {scale} bind def\n"
825 	  "/sd {setdash} bind def\n"
826 	  "/ff {findfont} bind def\n"
827 	  "/sf {setfont} bind def\n"
828 	  "/scf {scalefont} bind def\n"
829 	  "/sw {stringwidth pop} bind def\n"
830 	  "/tr {translate} bind def\n"
831 
832 	  "\n/ellipsedict 8 dict def\n"
833 	  "ellipsedict /mtrx matrix put\n"
834 	  "/ellipse\n"
835 	  "{ ellipsedict begin\n"
836           "   /endangle exch def\n"
837           "   /startangle exch def\n"
838           "   /yrad exch def\n"
839           "   /xrad exch def\n"
840           "   /y exch def\n"
841           "   /x exch def"
842 	  "   /savematrix mtrx currentmatrix def\n"
843           "   x y tr xrad yrad sc\n"
844           "   0 0 1 startangle endangle arc\n"
845           "   savematrix setmatrix\n"
846           "   end\n"
847 	  "} def\n\n"
848 	  "/mergeprocs {\n"
849 	  "dup length\n"
850 	  "3 -1 roll\n"
851 	  "dup\n"
852 	  "length\n"
853 	  "dup\n"
854 	  "5 1 roll\n"
855 	  "3 -1 roll\n"
856 	  "add\n"
857 	  "array cvx\n"
858 	  "dup\n"
859 	  "3 -1 roll\n"
860 	  "0 exch\n"
861 	  "putinterval\n"
862 	  "dup\n"
863 	  "4 2 roll\n"
864 	  "putinterval\n"
865 	  "} bind def\n");
866 
867 }
868 
869 /* helper function */
870 static void
print_reencode_font(FILE * file,char * fontname)871 print_reencode_font(FILE *file, char *fontname)
872 {
873   /* Don't reencode the Symbol font, as it doesn't work in latin1 encoding.
874    * Instead, just define Symbol-latin1 to be the same as Symbol. */
875   if (!strcmp(fontname, "Symbol"))
876     fprintf(file,
877 	    "/%s-latin1\n"
878 	    "    /%s findfont\n"
879 	    "definefont pop\n", fontname, fontname);
880   else
881     fprintf(file,
882 	    "/%s-latin1\n"
883 	    "    /%s findfont\n"
884 	    "    dup length dict begin\n"
885 	    "	{1 index /FID ne {def} {pop pop} ifelse} forall\n"
886 	    "	/Encoding isolatin1encoding def\n"
887 	    "    currentdict end\n"
888 	    "definefont pop\n", fontname, fontname);
889 }
890 
891 static void
dump_fonts(DiaPsRenderer * renderer)892 dump_fonts (DiaPsRenderer *renderer)
893 {
894   print_reencode_font(renderer->file, "Times-Roman");
895   print_reencode_font(renderer->file, "Times-Italic");
896   print_reencode_font(renderer->file, "Times-Bold");
897   print_reencode_font(renderer->file, "Times-BoldItalic");
898   print_reencode_font(renderer->file, "AvantGarde-Gothic");
899   print_reencode_font(renderer->file, "AvantGarde-BookOblique");
900   print_reencode_font(renderer->file, "AvantGarde-Demi");
901   print_reencode_font(renderer->file, "AvantGarde-DemiOblique");
902   print_reencode_font(renderer->file, "Bookman-Light");
903   print_reencode_font(renderer->file, "Bookman-LightItalic");
904   print_reencode_font(renderer->file, "Bookman-Demi");
905   print_reencode_font(renderer->file, "Bookman-DemiItalic");
906   print_reencode_font(renderer->file, "Courier");
907   print_reencode_font(renderer->file, "Courier-Oblique");
908   print_reencode_font(renderer->file, "Courier-Bold");
909   print_reencode_font(renderer->file, "Courier-BoldOblique");
910   print_reencode_font(renderer->file, "Helvetica");
911   print_reencode_font(renderer->file, "Helvetica-Oblique");
912   print_reencode_font(renderer->file, "Helvetica-Bold");
913   print_reencode_font(renderer->file, "Helvetica-BoldOblique");
914   print_reencode_font(renderer->file, "Helvetica-Narrow");
915   print_reencode_font(renderer->file, "Helvetica-Narrow-Oblique");
916   print_reencode_font(renderer->file, "Helvetica-Narrow-Bold");
917   print_reencode_font(renderer->file, "Helvetica-Narrow-BoldOblique");
918   print_reencode_font(renderer->file, "NewCenturySchlbk-Roman");
919   print_reencode_font(renderer->file, "NewCenturySchlbk-Italic");
920   print_reencode_font(renderer->file, "NewCenturySchlbk-Bold");
921   print_reencode_font(renderer->file, "NewCenturySchlbk-BoldItalic");
922   print_reencode_font(renderer->file, "Palatino-Roman");
923   print_reencode_font(renderer->file, "Palatino-Italic");
924   print_reencode_font(renderer->file, "Palatino-Bold");
925   print_reencode_font(renderer->file, "Palatino-BoldItalic");
926   print_reencode_font(renderer->file, "Symbol");
927   print_reencode_font(renderer->file, "ZapfChancery-MediumItalic");
928   print_reencode_font(renderer->file, "ZapfDingbats");
929 }
930 
931 static void
end_prolog(DiaPsRenderer * renderer)932 end_prolog (DiaPsRenderer *renderer)
933 {
934   gchar d1_buf[DTOSTR_BUF_SIZE];
935   gchar d2_buf[DTOSTR_BUF_SIZE];
936 
937   if (renderer_is_eps(renderer)) {
938     fprintf(renderer->file,
939             "%s %s scale\n",
940 	    psrenderer_dtostr(d1_buf, renderer->scale),
941 	    psrenderer_dtostr(d2_buf, -renderer->scale) );
942     fprintf(renderer->file,
943             "%s %s translate\n",
944 	    psrenderer_dtostr(d1_buf, -renderer->extent.left),
945 	    psrenderer_dtostr(d2_buf, -renderer->extent.bottom) );
946   } else {
947     /* done by BoundingBox above */
948   }
949 
950   fprintf(renderer->file,
951 	  "%%%%EndProlog\n\n\n");
952 }
953 
954 /* constructor */
955 static void
ps_renderer_init(GTypeInstance * instance,gpointer g_class)956 ps_renderer_init (GTypeInstance *instance, gpointer g_class)
957 {
958   DiaPsRenderer *renderer = DIA_PS_RENDERER (instance);
959 
960   renderer->file = NULL;
961 
962   renderer->lcolor.red = -1.0;
963 
964   renderer->dash_length = 1.0;
965   renderer->dot_length = 0.2;
966   renderer->saved_line_style = LINESTYLE_SOLID;
967   renderer->is_portrait = TRUE;
968 
969   renderer->scale = 28.346;
970 }
971 
972 /* GObject stuff */
973 static void dia_ps_renderer_class_init (DiaPsRendererClass *klass);
974 
975 static gpointer parent_class = NULL;
976 
977 GType
dia_ps_renderer_get_type(void)978 dia_ps_renderer_get_type (void)
979 {
980   static GType object_type = 0;
981 
982   if (!object_type)
983     {
984       static const GTypeInfo object_info =
985       {
986         sizeof (DiaPsRendererClass),
987         (GBaseInitFunc) NULL,
988         (GBaseFinalizeFunc) NULL,
989         (GClassInitFunc) dia_ps_renderer_class_init,
990         NULL,           /* class_finalize */
991         NULL,           /* class_data */
992         sizeof (DiaPsRenderer),
993         0,              /* n_preallocs */
994         ps_renderer_init       /* init */
995       };
996 
997       object_type = g_type_register_static (DIA_TYPE_RENDERER,
998                                             "DiaPsRenderer",
999                                             &object_info, 0);
1000     }
1001 
1002   return object_type;
1003 }
1004 
1005 static void
dia_ps_renderer_finalize(GObject * object)1006 dia_ps_renderer_finalize (GObject *object)
1007 {
1008   DiaPsRenderer *renderer = DIA_PS_RENDERER (object);
1009 
1010   g_free(renderer->title);
1011   /*  fclose(renderer->file);*/
1012 
1013   G_OBJECT_CLASS (parent_class)->finalize (object);
1014 }
1015 
1016 static void
dia_ps_renderer_class_init(DiaPsRendererClass * klass)1017 dia_ps_renderer_class_init (DiaPsRendererClass *klass)
1018 {
1019   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1020   DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
1021   DiaPsRendererClass *ps_renderer_class = DIA_PS_RENDERER_CLASS (klass);
1022 
1023   parent_class = g_type_class_peek_parent (klass);
1024 
1025   object_class->finalize = dia_ps_renderer_finalize;
1026 
1027   renderer_class->begin_render = begin_render;
1028   renderer_class->end_render   = end_render;
1029 
1030   renderer_class->set_linewidth  = set_linewidth;
1031   renderer_class->set_linecaps   = set_linecaps;
1032   renderer_class->set_linejoin   = set_linejoin;
1033   renderer_class->set_linestyle  = set_linestyle;
1034   renderer_class->set_dashlength = set_dashlength;
1035   renderer_class->set_fillstyle  = set_fillstyle;
1036   renderer_class->set_font       = set_font;
1037 
1038   renderer_class->draw_line    = draw_line;
1039   renderer_class->fill_polygon = fill_polygon;
1040   renderer_class->draw_arc     = draw_arc;
1041   renderer_class->fill_arc     = fill_arc;
1042   renderer_class->draw_ellipse = draw_ellipse;
1043   renderer_class->fill_ellipse = fill_ellipse;
1044   renderer_class->draw_string  = draw_string;
1045   renderer_class->draw_image   = draw_image;
1046 
1047   /* medium level functions */
1048   renderer_class->draw_bezier  = draw_bezier;
1049   renderer_class->fill_bezier  = fill_bezier;
1050   renderer_class->draw_rect = draw_rect;
1051   renderer_class->fill_rect = fill_rect;
1052   renderer_class->draw_polyline  = draw_polyline;
1053   renderer_class->draw_polygon   = draw_polygon;
1054 
1055   /* ps specific */
1056   ps_renderer_class->begin_prolog = begin_prolog;
1057   ps_renderer_class->dump_fonts = dump_fonts;
1058   ps_renderer_class->end_prolog = end_prolog;
1059 }
1060 
1061