Code

Removed spaces from font names in EPS export. Also removed the parentheses (they...
[inkscape.git] / src / extension / internal / ps.cpp
1 #define __SP_PS_C__
3 /** \file
4  * PostScript printing.
5  */
6 /*
7  * Authors:
8  *   Lauris Kaplinski <lauris@kaplinski.com>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Basic printing code, EXCEPT image and
12  * ascii85 filter is in public domain
13  *
14  * Image printing and Ascii85 filter:
15  *
16  * Copyright (C) 2006 Johan Engelen
17  * Copyright (C) 1997-98 Peter Kirchgessner
18  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
19  * George White <aa056@chebucto.ns.ca>
20  * Austin Donnelly <austin@gimp.org>
21  *
22  * Licensed under GNU GPL
23  */
25 /* Plain Print */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <signal.h>
32 #include <errno.h>
34 #include <libnr/n-art-bpath.h>
36 #include <glib/gmem.h>
37 #include <gtk/gtkstock.h>
38 #include <gtk/gtkvbox.h>
39 #include <gtk/gtkframe.h>
40 #include <gtk/gtkradiobutton.h>
41 #include <gtk/gtkcombo.h>
42 #include <gtk/gtklabel.h>
43 #include <gtk/gtkentry.h>
44 #include <gtk/gtktooltips.h>
46 #include <glibmm/i18n.h>
47 #include "display/nr-arena-item.h"
48 #include "display/canvas-bpath.h"
49 #include "sp-item.h"
50 #include "style.h"
51 #include "sp-linear-gradient.h"
52 #include "sp-radial-gradient.h"
54 #include "libnrtype/font-instance.h"
55 #include "libnrtype/font-style-to-pos.h"
57 #include <unit-constants.h>
59 #include "ps.h"
60 #include "extension/system.h"
61 #include "extension/print.h"
63 #include "io/sys.h"
65 namespace Inkscape {
66 namespace Extension {
67 namespace Internal {
69 PrintPS::PrintPS() :
70     _stream(NULL),
71     _dpi(72),
72     _bitmap(false)
73 {
74 }
76 PrintPS::~PrintPS(void)
77 {
78     /* fixme: should really use pclose for popen'd streams */
79     if (_stream) fclose(_stream);
81     /* restore default signal handling for SIGPIPE */
82 #if !defined(_WIN32) && !defined(__WIN32__)
83     (void) signal(SIGPIPE, SIG_DFL);
84 #endif
86     return;
87 }
89 unsigned int
90 PrintPS::setup(Inkscape::Extension::Print * mod)
91 {
92     static gchar const *const pdr[] = {"72", "75", "100", "144", "150", "200", "300", "360", "600", "1200", "2400", NULL};
94 #ifdef TED
95     Inkscape::XML::Node *repr = ((SPModule *) mod)->repr;
96 #endif
98     unsigned int ret = FALSE;
100     /* Create dialog */
101     GtkTooltips *tt = gtk_tooltips_new();
102     g_object_ref((GObject *) tt);
103     gtk_object_sink((GtkObject *) tt);
105     GtkWidget *dlg = gtk_dialog_new_with_buttons(_("Print Destination"),
106 //            SP_DT_WIDGET(SP_ACTIVE_DESKTOP)->window,
107             NULL,
108             (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT),
109             GTK_STOCK_CANCEL,
110             GTK_RESPONSE_CANCEL,
111             GTK_STOCK_PRINT,
112             GTK_RESPONSE_OK,
113             NULL);
115     gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);
117     GtkWidget *vbox = GTK_DIALOG(dlg)->vbox;
118     gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
119     /* Print properties frame */
120     GtkWidget *f = gtk_frame_new(_("Print properties"));
121     gtk_box_pack_start(GTK_BOX(vbox), f, FALSE, FALSE, 4);
122     GtkWidget *vb = gtk_vbox_new(FALSE, 4);
123     gtk_container_add(GTK_CONTAINER(f), vb);
124     gtk_container_set_border_width(GTK_CONTAINER(vb), 4);
125     /* Print type */
126     bool const p2bm = mod->get_param_bool("bitmap");
127     GtkWidget *rb = gtk_radio_button_new_with_label(NULL, _("Print using PostScript operators"));
128     gtk_tooltips_set_tip((GtkTooltips *) tt, rb,
129                          _("Use PostScript vector operators. The resulting image is usually smaller "
130                            "in file size and can be arbitrarily scaled, but alpha transparency "
131                            "and patterns will be lost."), NULL);
132     if (!p2bm) gtk_toggle_button_set_active((GtkToggleButton *) rb, TRUE);
133     gtk_box_pack_start(GTK_BOX(vb), rb, FALSE, FALSE, 0);
134     rb = gtk_radio_button_new_with_label(gtk_radio_button_get_group((GtkRadioButton *) rb), _("Print as bitmap"));
135     gtk_tooltips_set_tip((GtkTooltips *) tt, rb,
136                          _("Print everything as bitmap. The resulting image is usually larger "
137                            "in file size and cannot be arbitrarily scaled without quality loss, "
138                            "but all objects will be rendered exactly as displayed."), NULL);
139     if (p2bm) gtk_toggle_button_set_active((GtkToggleButton *) rb, TRUE);
140     gtk_box_pack_start(GTK_BOX(vb), rb, FALSE, FALSE, 0);
141     /* Resolution */
142     GtkWidget *hb = gtk_hbox_new(FALSE, 4);
143     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 0);
144     GtkWidget *combo = gtk_combo_new();
145     gtk_combo_set_value_in_list(GTK_COMBO(combo), FALSE, FALSE);
146     gtk_combo_set_use_arrows(GTK_COMBO(combo), TRUE);
147     gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);
148     gtk_widget_set_size_request(combo, 64, -1);
149     gtk_tooltips_set_tip((GtkTooltips *) tt, GTK_COMBO(combo)->entry,
150                          _("Preferred resolution (dots per inch) of bitmap"), NULL);
151     /* Setup strings */
152     GList *sl = NULL;
153     for (unsigned i = 0; pdr[i] != NULL; i++) {
154         sl = g_list_prepend(sl, (gpointer) pdr[i]);
155     }
156     sl = g_list_reverse(sl);
157     gtk_combo_set_popdown_strings(GTK_COMBO(combo), sl);
158     g_list_free(sl);
159     if (1) {
160         gchar const *val = mod->get_param_string("resolution");
161         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), val);
162     }
163     gtk_box_pack_end(GTK_BOX(hb), combo, FALSE, FALSE, 0);
164     GtkWidget *l = gtk_label_new(_("Resolution:"));
165     gtk_box_pack_end(GTK_BOX(hb), l, FALSE, FALSE, 0);
167     /* Print destination frame */
168     f = gtk_frame_new(_("Print destination"));
169     gtk_box_pack_start(GTK_BOX(vbox), f, FALSE, FALSE, 4);
170     vb = gtk_vbox_new(FALSE, 4);
171     gtk_container_add(GTK_CONTAINER(f), vb);
172     gtk_container_set_border_width(GTK_CONTAINER(vb), 4);
174     l = gtk_label_new(_("Printer name (as given by lpstat -p);\n"
175                         "leave empty to use the system default printer.\n"
176                         "Use '> filename' to print to file.\n"
177                         "Use '| prog arg...' to pipe to a program."));
178     gtk_box_pack_start(GTK_BOX(vb), l, FALSE, FALSE, 0);
180     GtkWidget *e = gtk_entry_new();
181     if (1) {
182         gchar const *val = mod->get_param_string("destination");
183         gtk_entry_set_text(GTK_ENTRY(e), ( val != NULL
184                                            ? val
185                                            : "" ));
186     }
187     gtk_box_pack_start(GTK_BOX(vb), e, FALSE, FALSE, 0);
189     // pressing enter in the destination field is the same as clicking Print:
190     gtk_entry_set_activates_default(GTK_ENTRY(e), TRUE);
192     gtk_widget_show_all(vbox);
194     int const response = gtk_dialog_run(GTK_DIALOG(dlg));
196     g_object_unref((GObject *) tt);
198     if (response == GTK_RESPONSE_OK) {
199         gchar const *fn;
200         char const *sstr;
202         _bitmap = gtk_toggle_button_get_active((GtkToggleButton *) rb);
203         sstr = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry));
204         _dpi = (unsigned int) MAX((int)(atof(sstr)), 1);
205         /* Arrgh, have to do something */
206         fn = gtk_entry_get_text(GTK_ENTRY(e));
207         /* skip leading whitespace, bug #1068483 */
208         while (fn && *fn==' ') { fn++; }
209         /* g_print("Printing to %s\n", fn); */
211         mod->set_param_bool("bitmap", _bitmap);
212         mod->set_param_string("resolution", (gchar *)sstr);
213         mod->set_param_string("destination", (gchar *)fn);
214         ret = TRUE;
215     }
217     gtk_widget_destroy(dlg);
219     return ret;
222 unsigned int
223 PrintPS::begin(Inkscape::Extension::Print *mod, SPDocument *doc)
225     gboolean epsexport = false;
227     _latin1_encoded_fonts.clear();
228     _newlatin1font_proc_defined = false;
230     FILE *osf = NULL;
231     FILE *osp = NULL;
233     gsize bytesRead = 0;
234     gsize bytesWritten = 0;
235     GError *error = NULL;
236     gchar const *utf8_fn = mod->get_param_string("destination");
237     gchar *local_fn = g_filename_from_utf8( utf8_fn,
238                                             -1,  &bytesRead,  &bytesWritten, &error);
239     gchar const *fn = local_fn;
241     /* TODO: Replace the below fprintf's with something that does the right thing whether in
242      * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of
243      * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the
244      * return code.
245      */
246     if (fn != NULL) {
247         if (*fn == '|') {
248             fn += 1;
249             while (isspace(*fn)) fn += 1;
250 #ifndef WIN32
251             osp = popen(fn, "w");
252 #else
253             osp = _popen(fn, "w");
254 #endif
255             if (!osp) {
256                 fprintf(stderr, "inkscape: popen(%s): %s\n",
257                         fn, strerror(errno));
258                 return 0;
259             }
260             _stream = osp;
261         } else if (*fn == '>') {
262             fn += 1;
263             epsexport = g_str_has_suffix(fn,".eps");
264             while (isspace(*fn)) fn += 1;
265             Inkscape::IO::dump_fopen_call(fn, "K");
266             osf = Inkscape::IO::fopen_utf8name(fn, "w+");
267             if (!osf) {
268                 fprintf(stderr, "inkscape: fopen(%s): %s\n",
269                         fn, strerror(errno));
270                 return 0;
271             }
272             _stream = osf;
273         } else {
274             /* put cwd stuff in here */
275             gchar *qn = ( *fn
276                           ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */
277                           : g_strdup("lpr") );
278 #ifndef WIN32
279             osp = popen(qn, "w");
280 #else
281             osp = _popen(qn, "w");
282 #endif
283             if (!osp) {
284                 fprintf(stderr, "inkscape: popen(%s): %s\n",
285                         qn, strerror(errno));
286                 return 0;
287             }
288             g_free(qn);
289             _stream = osp;
290         }
291     }
293     g_free(local_fn);
295     if (_stream) {
296         /* fixme: this is kinda icky */
297 #if !defined(_WIN32) && !defined(__WIN32__)
298         (void) signal(SIGPIPE, SIG_IGN);
299 #endif
300     }
302     int const res = fprintf(_stream, ( epsexport
303                                        ? "%%!PS-Adobe-3.0 EPSF-3.0\n"
304                                        : "%%!PS-Adobe-3.0\n" ));
305     /* flush this to test output stream as early as possible */
306     if (fflush(_stream)) {
307         /*g_print("caught error in sp_module_print_plain_begin\n");*/
308         if (ferror(_stream)) {
309             g_print("Error %d on output stream: %s\n", errno,
310                     g_strerror(errno));
311         }
312         g_print("Printing failed\n");
313         /* fixme: should use pclose() for pipes */
314         fclose(_stream);
315         _stream = NULL;
316         fflush(stdout);
317         return 0;
318     }
320     // width and height in pt
321     _width = sp_document_width(doc) * PT_PER_PX;
322     _height = sp_document_height(doc) * PT_PER_PX;
324     NRRect d;
325     bool   pageBoundingBox;
326     bool   pageLandscape;
327     pageBoundingBox = mod->get_param_bool("pageBoundingBox");
328     // printf("Page Bounding Box: %s\n", pageBoundingBox ? "TRUE" : "FALSE");
329     if (pageBoundingBox) {
330         d.x0 = d.y0 = 0;
331         d.x1 = ceil(_width);
332         d.y1 = ceil(_height);
333     } else {
334         SPItem* doc_item = SP_ITEM(sp_document_root(doc));
335         sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE);
336         // convert from px to pt
337         d.x0 *= PT_PER_PX;
338         d.x1 *= PT_PER_PX;
339         d.y0 *= PT_PER_PX;
340         d.y1 *= PT_PER_PX;
341     }
343     Inkscape::SVGOStringStream os;
344     if (res >= 0) {
346         os << "%%Creator: " << PACKAGE_STRING << "\n";
347         // This will become problematic if inkscape gains the
348         // ability to handle multi paged documents. If this is
349         // the case the %%Orientation: comments should be
350         // renamed to %%PageOrientation: and moved to the
351         // respective pages.
352         os << "%%Pages: 1\n";
354         // 2004 Dec 10, BFC:
355         // The point of the following code is (1) to do the thing that's expected by users
356         // who have done File>New>A4_landscape or ...letter_landscape (i.e., rotate
357         // the output), while (2) not messing up users who simply want their output wider
358         // than it is tall (e.g., small figures for inclusion in LaTeX).
359         // The original patch by WQ only had the w>h condition.
360         {
361              double w = (d.x1 - d.x0); // width and height of bounding box, in pt
362              double h = (d.y1 - d.y0);
363              pageLandscape = (
364                  (w > 0. && h > 0.) // empty documents fail this sanity check, have w<0, h<0
365                  && (w > h)   // implies, but does not prove, the user wanted landscape
366                  && (w > 600) // approximate maximum printable width of an A4
367                  && (!epsexport) // eps should always be portrait
368              )
369              ? true : false;
370         }
372         if (pageLandscape) {
373             os << "%%Orientation: Landscape\n";
374             os << "%%BoundingBox: " << (int) (_height - d.y1) << " "
375                << (int) d.x0 << " "
376                << (int) ceil(_height - d.y0) << " "
377                << (int) ceil(d.x1) << "\n";
378             // According to Mike Sweet (the author of CUPS)
379             // HiResBoundingBox is only appropriate
380             // for EPS files. This means that we should
381             // distinguish if we export to ps or eps here.
382             // FIXME: I couldn't find HiResBoundingBox in the PS
383             // reference manual, so I guess we should skip
384             // it.
385             os << "%%HiResBoundingBox: " << (_height - d.y1) << " "
386                << d.x0 << " "
387                << (_height - d.y0) << " "
388                << d.x1 << "\n";
389             os << "%%DocumentMedia: plain "
390                << (int) ceil(_height) << " "
391                << (int) ceil(_width) << " "
392                << "0 () ()\n";
393         } else {
394             os << "%%Orientation: Portrait\n";
395             os << "%%BoundingBox: " << (int) d.x0 << " "
396                << (int) d.y0 << " "
397                << (int) ceil(d.x1) << " "
398                << (int) ceil(d.y1) << "\n";
399             os << "%%HiResBoundingBox: " << d.x0 << " "
400                << d.y0 << " "
401                << d.x1 << " "
402                << d.y1 << "\n";
403             os << "%%DocumentMedia: plain "
404                << (int) ceil(_width) << " "
405                << (int) ceil(_height) << " "
406                << "0 () ()\n";
407         }
409         os << "%%EndComments\n";
410         // This will become problematic if we print multi paged documents:
411         os << "%%Page: 1 1\n";
413         if (pageLandscape) {
414             os << "90 rotate\n";
415             if (_bitmap) {
416                 os << "0 " << (int) -ceil(_height) << " translate\n";
417             }
418         } else {
419             if (!_bitmap) {
420                 os << "0 " << (int) ceil(_height) << " translate\n";
421             }
422         }
424         if (!_bitmap) {
425             os << PT_PER_PX << " " << -PT_PER_PX << " scale\n";
426             // from now on we can output px, but they will be treated as pt
427         }
428     }
430     os << "0 0 0 setrgbcolor\n"
431        << "[] 0 setdash\n"
432        << "1 setlinewidth\n"
433        << "0 setlinejoin\n"
434        << "0 setlinecap\n";
436     /* FIXME: This function is declared to return unsigned, whereas fprintf returns a signed int *
437      * that can be zero if the first fprintf failed (os is empty) or "negative" (i.e. very positive
438      * in unsigned int interpretation) if the first fprintf failed but this one succeeds, or
439      * positive if both succeed. */
440     return fprintf(_stream, "%s", os.str().c_str());
443 unsigned int
444 PrintPS::finish(Inkscape::Extension::Print *mod)
446     if (!_stream) return 0;
448     if (_bitmap) {
449         double const dots_per_pt = _dpi / PT_PER_IN;
451         double const x0 = 0.0;
452         double const y0 = 0.0;
453         double const x1 = x0 + _width;
454         double const y1 = y0 + _height;
456         /* Bitmap width/height in bitmap dots. */
457         int const width = (int) (_width * dots_per_pt + 0.5);
458         int const height = (int) (_height * dots_per_pt + 0.5);
460         NRMatrix affine;
461         affine.c[0] = width / ((x1 - x0) * PX_PER_PT);
462         affine.c[1] = 0.0;
463         affine.c[2] = 0.0;
464         affine.c[3] = height / ((y1 - y0) * PX_PER_PT);
465         affine.c[4] = -affine.c[0] * x0;
466         affine.c[5] = -affine.c[3] * y0;
468         nr_arena_item_set_transform(mod->root, &affine);
470         guchar *const px = g_new(guchar, 4 * width * 64);
472         for (int y = 0; y < height; y += 64) {
473             /* Set area of interest. */
474             NRRectL bbox;
475             bbox.x0 = 0;
476             bbox.y0 = y;
477             bbox.x1 = width;
478             bbox.y1 = MIN(height, y + 64);
480             /* Update to renderable state. */
481             NRGC gc(NULL);
482             nr_matrix_set_identity(&gc.transform);
483             nr_arena_item_invoke_update(mod->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
484             /* Render */
485             /* This should take guchar* instead of unsigned char*) */
486             NRPixBlock pb;
487             nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N,
488                                      bbox.x0, bbox.y0, bbox.x1, bbox.y1,
489                                      (guchar*)px, 4 * width, FALSE, FALSE);
490             memset(px, 0xff, 4 * width * 64);
491             nr_arena_item_invoke_render(mod->root, &bbox, &pb, 0);
492             /* Blitter goes here */
493             NRMatrix imgt;
494             imgt.c[0] = (bbox.x1 - bbox.x0) / dots_per_pt;
495             imgt.c[1] = 0.0;
496             imgt.c[2] = 0.0;
497             imgt.c[3] = (bbox.y1 - bbox.y0) / dots_per_pt;
498             imgt.c[4] = 0.0;
499             imgt.c[5] = _height - y / dots_per_pt - (bbox.y1 - bbox.y0) / dots_per_pt;
501             print_image(_stream, px, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, 4 * width, &imgt);
502         }
504         g_free(px);
505     }
507     fprintf(_stream, "showpage\n");
508     int const res = fprintf(_stream, "%%%%EOF\n");
510     /* Flush stream to be sure. */
511     (void) fflush(_stream);
513     /* fixme: should really use pclose for popen'd streams */
514     fclose(_stream);
515     _stream = 0;
516     _latin1_encoded_fonts.clear();
518     return res;
521 unsigned int
522 PrintPS::bind(Inkscape::Extension::Print *mod, NRMatrix const *transform, float opacity)
524     if (!_stream) return 0;  // XXX: fixme, returning -1 as unsigned.
525     if (_bitmap) return 0;
527     Inkscape::SVGOStringStream os;
528     os << "gsave [" << transform->c[0] << " "
529        << transform->c[1] << " "
530        << transform->c[2] << " "
531        << transform->c[3] << " "
532        << transform->c[4] << " "
533        << transform->c[5] << "] concat\n";
535     return fprintf(_stream, "%s", os.str().c_str());
538 unsigned int
539 PrintPS::release(Inkscape::Extension::Print *mod)
541     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.
542     if (_bitmap) return 0;
544     return fprintf(_stream, "grestore\n");
547 unsigned int
548 PrintPS::comment(Inkscape::Extension::Print *mod, char const *comment)
550     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.
551     if (_bitmap) return 0;
553     return fprintf(_stream, "%%! %s\n",comment);
556 void
557 PrintPS::print_fill_style(SVGOStringStream &os, SPStyle const *const style, NRRect const *pbox)
559     g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR
560                       || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
561                            && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) );
563     if (style->fill.type == SP_PAINT_TYPE_COLOR) {
564         float rgb[3];
565         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);
567         os << rgb[0] << " " << rgb[1] << " " << rgb[2] << " setrgbcolor\n";
569     } else {
570         g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
571                   && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );
573         if (SP_IS_LINEARGRADIENT(SP_STYLE_FILL_SERVER(style))) {
575             SPLinearGradient *lg = SP_LINEARGRADIENT(SP_STYLE_FILL_SERVER(style));
576             NR::Point p1 (lg->x1.computed, lg->y1.computed);
577             NR::Point p2 (lg->x2.computed, lg->y2.computed);
578             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
579                 // convert to userspace
580                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);
581                 p1 *= bbox2user;
582                 p2 *= bbox2user;
583             }
585             os << "<<\n/ShadingType 2\n/ColorSpace /DeviceRGB\n";
586             os << "/Coords [" << p1[NR::X] << " " << p1[NR::Y] << " " << p2[NR::X] << " " << p2[NR::Y] <<"]\n";
587             os << "/Extend [true true]\n";
588             os << "/Domain [0 1]\n";
589             os << "/Function <<\n/FunctionType 3\n/Functions\n[\n";
591             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built
592             for (unsigned i = 0; i + 1 < lg->vector.stops.size(); i++) {
593                 float rgb[3];
594                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);
595                 os << "<<\n/FunctionType 2\n/Domain [0 1]\n";
596                 os << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";
597                 sp_color_get_rgb_floatv(&lg->vector.stops[i+1].color, rgb);
598                 os << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";
599                 os << "/N 1\n>>\n";
600             }
601             os << "]\n/Domain [0 1]\n";
602             os << "/Bounds [ ";
603             for (unsigned i = 0; i + 2 < lg->vector.stops.size(); i++) {
604                 os << lg->vector.stops[i+1].offset <<" ";
605             }
606             os << "]\n";
607             os << "/Encode [ ";
608             for (unsigned i = 0; i + 1 < lg->vector.stops.size(); i++) {
609                 os << "0 1 ";
610             }
611             os << "]\n";
612             os << ">>\n>>\n";
614         } else if (SP_IS_RADIALGRADIENT(SP_STYLE_FILL_SERVER(style))) {
616             SPRadialGradient *rg = SP_RADIALGRADIENT(SP_STYLE_FILL_SERVER(style));
617             NR::Point c(rg->cx.computed, rg->cy.computed);
618             NR::Point f(rg->fx.computed, rg->fy.computed);
619             double r = rg->r.computed;
620             if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
621                 // convert to userspace
622                 NR::Matrix const bbox2user(pbox->x1 - pbox->x0, 0,
623                                            0, pbox->y1 - pbox->y0,
624                                            pbox->x0, pbox->y0);
625                 c *= bbox2user;
626                 f *= bbox2user;
627                 r *= bbox2user.expansion();
628             }
630             os << "<<\n/ShadingType 3\n/ColorSpace /DeviceRGB\n";
631             os << "/Coords ["<< f[NR::X] <<" "<< f[NR::Y] <<" 0 "<< c[NR::X] <<" "<< c[NR::Y] <<" "<< r <<"]\n";
632             os << "/Extend [true true]\n";
633             os << "/Domain [0 1]\n";
634             os << "/Function <<\n/FunctionType 3\n/Functions\n[\n";
636             sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built
637             for (unsigned i = 0; i + 1 < rg->vector.stops.size(); i++) {
638                 float rgb[3];
639                 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);
640                 os << "<<\n/FunctionType 2\n/Domain [0 1]\n";
641                 os << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";
642                 sp_color_get_rgb_floatv(&rg->vector.stops[i+1].color, rgb);
643                 os << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";
644                 os << "/N 1\n>>\n";
645             }
646             os << "]\n/Domain [0 1]\n";
647             os << "/Bounds [ ";
648             for (unsigned i = 0; i + 2 < rg->vector.stops.size(); i++) {
649                 os << rg->vector.stops[i+1].offset << " ";
650             }
651             os << "]\n";
652             os << "/Encode [ ";
653             for (unsigned i = 0; i + 1 < rg->vector.stops.size(); i++) {
654                 os << "0 1 ";
655             }
656             os << "]\n";
657             os << ">>\n>>\n";
658         }
659     }
662 void
663 PrintPS::print_stroke_style(SVGOStringStream &os, SPStyle const *style)
665     float rgb[3];
666     sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);
668     os << rgb[0] << " " << rgb[1] << " " << rgb[2] << " setrgbcolor\n";
670     // There are rare cases in which for a solid line stroke_dasharray_set is true. To avoid
671     // invalid PS-lines such as "[0.0000000 0.0000000] 0.0000000 setdash", which should be "[] 0 setdash",
672     // we first check if all components of stroke_dash.dash are 0.
673     bool LineSolid = true;
674     if (style->stroke_dash.n_dash   &&
675         style->stroke_dash.dash       )
676     {
677         int i = 0;
678         while (LineSolid && (i < style->stroke_dash.n_dash)) {
679                 if (style->stroke_dash.dash[i] > 0.00000001)
680                     LineSolid = false;
681                 i++;
682         }
683         if (!LineSolid) {
684             os << "[";
685             for (i = 0; i < style->stroke_dash.n_dash; i++) {
686                 if (i > 0) {
687                     os << " ";
688                 }
689                 os << style->stroke_dash.dash[i];
690             }
691             os << "] " << style->stroke_dash.offset << " setdash\n";
692         } else {
693             os << "[] 0 setdash\n";
694         }
695     } else {
696         os << "[] 0 setdash\n";
697     }
699     os << style->stroke_width.computed << " setlinewidth\n";
700     os << style->stroke_linejoin.computed << " setlinejoin\n";
701     os << style->stroke_linecap.computed << " setlinecap\n";
705 unsigned int
706 PrintPS::fill(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *const style,
707               NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)
709     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.
710     if (_bitmap) return 0;
712     if ( style->fill.type == SP_PAINT_TYPE_COLOR
713          || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
714               && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) )
715     {
716         Inkscape::SVGOStringStream os;
718         os << "gsave\n";
720         print_fill_style(os, style, pbox);
722         print_bpath(os, bpath->path);
724         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {
725             if (style->fill.type == SP_PAINT_TYPE_COLOR) {
726                 os << "eofill\n";
727             } else {
728                 g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
729                           && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );
730                 SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
731                 os << "eoclip\n";
732                 if (g->gradientTransform_set) {
733                     os << "gsave [" << g->gradientTransform[0] << " " << g->gradientTransform[1]
734                         << " " << g->gradientTransform[2] << " " << g->gradientTransform[3]
735                         << " " << g->gradientTransform[4] << " " << g->gradientTransform[5] << "] concat\n";
736                 }
737                 os << "shfill\n";
738                 if (g->gradientTransform_set) {
739                     os << "grestore\n";
740                 }
741             }
742         } else {
743             if (style->fill.type == SP_PAINT_TYPE_COLOR) {
744                 os << "fill\n";
745             } else {
746                 g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
747                           && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );
748                 SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
749                 os << "clip\n";
750                 if (g->gradientTransform_set) {
751                     os << "gsave [" << g->gradientTransform[0] << " " << g->gradientTransform[1]
752                         << " " << g->gradientTransform[2] << " " << g->gradientTransform[3]
753                         << " " << g->gradientTransform[4] << " " << g->gradientTransform[5] << "] concat\n";
754                 }
755                 os << "shfill\n";
756                 if (g->gradientTransform_set) {
757                     os << "grestore\n";
758                 }
759             }
760         }
762         os << "grestore\n";
764         fprintf(_stream, "%s", os.str().c_str());
765     }
767     return 0;
771 unsigned int
772 PrintPS::stroke(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style,
773                 NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)
775     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.
776     if (_bitmap) return 0;
778     if (style->stroke.type == SP_PAINT_TYPE_COLOR) {
779         Inkscape::SVGOStringStream os;
781         print_stroke_style(os, style);
783         print_bpath(os, bpath->path);
785         os << "stroke\n";
787         fprintf(_stream, "%s", os.str().c_str());
788     }
790     return 0;
793 unsigned int
794 PrintPS::image(Inkscape::Extension::Print *mod, guchar *px, unsigned int w, unsigned int h, unsigned int rs,
795                NRMatrix const *transform, SPStyle const *style)
797     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.
798     if (_bitmap) return 0;
800     return print_image(_stream, px, w, h, rs, transform);
803 char const *
804 PrintPS::PSFontName(SPStyle const *style)
806     font_instance *tf = (font_factory::Default())->Face(style->text->font_family.value, font_style_to_pos(*style));
808     char const *n;
809     char name_buf[256];
811     // PS does not like spaces in fontnames, replace them with the usual dashes.
813     if (tf) {
814         tf->PSName(name_buf, sizeof(name_buf));
815         n = g_strdelimit(name_buf, " ", '-');
816         tf->Unref();
817     } else {
818         // this system does not have this font, so just use the name from SVG in the hope that PS interpreter will make sense of it
819         bool i = (style->font_style.value == SP_CSS_FONT_STYLE_ITALIC);
820         bool o = (style->font_style.value == SP_CSS_FONT_STYLE_OBLIQUE);
821         bool b = (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) ||
822             (style->font_weight.value >= SP_CSS_FONT_WEIGHT_500 && style->font_weight.value <= SP_CSS_FONT_WEIGHT_900);
824         n = g_strdup_printf("%s%s%s%s",
825                             g_strdelimit(style->text->font_family.value, " ", '-'), 
826                             (b || i || o) ? "-" : "",
827                             (b) ? "Bold" : "",
828                             (i) ? "Italic" : ((o) ? "Oblique" : "") );
829     }
831     return g_strdup(n);
835 unsigned int
836 PrintPS::text(Inkscape::Extension::Print *mod, char const *text, NR::Point p,
837               SPStyle const *const style)
839     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.
840     if (_bitmap) return 0;
842     Inkscape::SVGOStringStream os;
844     // Escape chars
845     Inkscape::SVGOStringStream escaped_text;
846     escaped_text << std::oct;
847     for (gchar const *p_text = text ; *p_text ; p_text = g_utf8_next_char(p_text)) {
848         gunichar const c = g_utf8_get_char(p_text);
849         if (c == '\\' || c == ')' || c == '(')
850             escaped_text << '\\' << static_cast<char>(c);
851         else if (c >= 0x80)
852             escaped_text << '\\' << c;
853         else
854             escaped_text << static_cast<char>(c);
855     }
857     os << "gsave\n";
859     // set font
860     char const *fn = PSFontName(style);
861     if (_latin1_encoded_fonts.find(fn) == _latin1_encoded_fonts.end()) {
862         if (!_newlatin1font_proc_defined) {
863             // input: newfontname, existingfontname
864             // output: new font object, also defined to newfontname
865             os << "/newlatin1font "         // name of the proc
866                   "{findfont dup length dict copy "     // load the font and create a copy of it
867                   "dup /Encoding ISOLatin1Encoding put "     // change the encoding in the copy
868                   "definefont} def\n";      // create the new font and leave it on the stack, define the proc
869             _newlatin1font_proc_defined = true;
870         }
871         os << "/" << fn << "-ISOLatin1 /" << fn << " newlatin1font\n";
872         _latin1_encoded_fonts.insert(fn);
873     } else
874         os << "/" << fn << "-ISOLatin1 findfont\n";
875     os << style->font_size.computed << " scalefont\n";
876     os << "setfont\n";
877     g_free((void *) fn);
879     if ( style->fill.type == SP_PAINT_TYPE_COLOR
880          || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER
881               && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) )
882     {
883         // set fill style
884         print_fill_style(os, style, NULL);
885         // FIXME: we don't know the pbox of text, so have to pass NULL. This means gradients with
886         // bbox units won't work with text. However userspace gradients don't work with text either
887         // (text is black) for some reason.
889         os << "newpath\n";
890         os << p[NR::X] << " " << p[NR::Y] << " moveto\n";
891         os << "(" << escaped_text.str() << ") show\n";
892     }
894     if (style->stroke.type == SP_PAINT_TYPE_COLOR) {
896         // set stroke style
897         print_stroke_style(os, style);
899         // paint stroke
900         os << "newpath\n";
901         os << p[NR::X] << " " << p[NR::Y] << " moveto\n";
902         os << "(" << escaped_text.str() << ") false charpath stroke\n";
903     }
905     os << "grestore\n";
907     fprintf(_stream, "%s", os.str().c_str());
909     return 0;
914 /* PostScript helpers */
916 void
917 PrintPS::print_bpath(SVGOStringStream &os, NArtBpath const *bp)
919     os << "newpath\n";
920     bool closed = false;
921     while (bp->code != NR_END) {
922         switch (bp->code) {
923             case NR_MOVETO:
924                 if (closed) {
925                     os << "closepath\n";
926                 }
927                 closed = true;
928                 os << bp->x3 << " " << bp->y3 << " moveto\n";
929                 break;
930             case NR_MOVETO_OPEN:
931                 if (closed) {
932                     os << "closepath\n";
933                 }
934                 closed = false;
935                 os << bp->x3 << " " << bp->y3 << " moveto\n";
936                 break;
937             case NR_LINETO:
938                 os << bp->x3 << " " << bp->y3 << " lineto\n";
939                 break;
940             case NR_CURVETO:
941                 os << bp->x1 << " " << bp->y1 << " "
942                    << bp->x2 << " " << bp->y2 << " "
943                    << bp->x3 << " " << bp->y3 << " curveto\n";
944                 break;
945             default:
946                 break;
947         }
948         bp += 1;
949     }
950     if (closed) {
951         os << "closepath\n";
952     }
955 /* The following code is licensed under GNU GPL.
956 ** The packbits, ascii85 and imaging printing code
957 ** is from the gimp's postscript.c.
958 */
960 /**
961 * \param nin Number of bytes of source data.
962 * \param src Source data.
963 * \param nout Number of output bytes.
964 * \param dst Buffer for output.
965 */
966 void
967 PrintPS::compress_packbits(int nin,
968                            guchar *src,
969                            int *nout,
970                            guchar *dst)
973     register guchar c;
974     int nrepeat, nliteral;
975     guchar *run_start;
976     guchar *start_dst = dst;
977     guchar *last_literal = NULL;
979     for (;;) {
980         if (nin <= 0) break;
982         run_start = src;
983         c = *run_start;
985         /* Search repeat bytes */
986         if ((nin > 1) && (c == src[1])) {
987             nrepeat = 1;
988             nin -= 2;
989             src += 2;
990             while ((nin > 0) && (c == *src)) {
991                 nrepeat++;
992                 src++;
993                 nin--;
994                 if (nrepeat == 127) break; /* Maximum repeat */
995             }
997             /* Add two-byte repeat to last literal run ? */
998             if ( (nrepeat == 1)
999                  && (last_literal != NULL) && (((*last_literal)+1)+2 <= 128) )
1000             {
1001                 *last_literal += 2;
1002                 *(dst++) = c;
1003                 *(dst++) = c;
1004                 continue;
1005             }
1007             /* Add repeat run */
1008             *(dst++) = (guchar)((-nrepeat) & 0xff);
1009             *(dst++) = c;
1010             last_literal = NULL;
1011             continue;
1012         }
1013         /* Search literal bytes */
1014         nliteral = 1;
1015         nin--;
1016         src++;
1018         for (;;) {
1019             if (nin <= 0) break;
1021             if ((nin >= 2) && (src[0] == src[1])) /* A two byte repeat ? */
1022                 break;
1024             nliteral++;
1025             nin--;
1026             src++;
1027             if (nliteral == 128) break; /* Maximum literal run */
1028         }
1030         /* Could be added to last literal run ? */
1031         if ((last_literal != NULL) && (((*last_literal)+1)+nliteral <= 128)) {
1032             *last_literal += nliteral;
1033         } else {
1034             last_literal = dst;
1035             *(dst++) = (guchar)(nliteral-1);
1036         }
1037         while (nliteral-- > 0) *(dst++) = *(run_start++);
1038     }
1039     *nout = dst - start_dst;
1042 void
1043 PrintPS::ascii85_init(void)
1045     ascii85_len = 0;
1046     ascii85_linewidth = 0;
1049 void
1050 PrintPS::ascii85_flush(SVGOStringStream &os)
1052     char c[5];
1053     bool const zero_case = (ascii85_buf == 0);
1054     static int const max_linewidth = 75;
1056     for (int i = 4; i >= 0; i--) {
1057         c[i] = (ascii85_buf % 85) + '!';
1058         ascii85_buf /= 85;
1059     }
1060     /* check for special case: "!!!!!" becomes "z", but only if not
1061      * at end of data. */
1062     if (zero_case && (ascii85_len == 4)) {
1063         if (ascii85_linewidth >= max_linewidth) {
1064             os << '\n';
1065             ascii85_linewidth = 0;
1066         }
1067         os << 'z';
1068         ascii85_linewidth++;
1069     } else {
1070         for (int i = 0; i < ascii85_len+1; i++) {
1071             if ((ascii85_linewidth >= max_linewidth) && (c[i] != '%')) {
1072                 os << '\n';
1073                 ascii85_linewidth = 0;
1074             }
1075             os << c[i];
1076             ascii85_linewidth++;
1077         }
1078     }
1080     ascii85_len = 0;
1081     ascii85_buf = 0;
1084 inline void
1085 PrintPS::ascii85_out(guchar byte, SVGOStringStream &os)
1087     if (ascii85_len == 4)
1088         ascii85_flush(os);
1090     ascii85_buf <<= 8;
1091     ascii85_buf |= byte;
1092     ascii85_len++;
1095 void
1096 PrintPS::ascii85_nout(int n, guchar *uptr, SVGOStringStream &os)
1098     while (n-- > 0) {
1099         ascii85_out(*uptr, os);
1100         uptr++;
1101     }
1104 void
1105 PrintPS::ascii85_done(SVGOStringStream &os)
1107     if (ascii85_len) {
1108         /* zero any unfilled buffer portion, then flush */
1109         ascii85_buf <<= (8 * (4-ascii85_len));
1110         ascii85_flush(os);
1111     }
1113     os << "~>\n";
1116 unsigned int
1117 PrintPS::print_image(FILE *ofp, guchar *px, unsigned int width, unsigned int height, unsigned int rs,
1118                      NRMatrix const *transform)
1120     Inkscape::SVGOStringStream os;
1122     os << "gsave\n";
1124     os << "[" << transform->c[0] << " "
1125        << transform->c[1] << " "
1126        << transform->c[2] << " "
1127        << transform->c[3] << " "
1128        << transform->c[4] << " "
1129        << transform->c[5] << "] concat\n";
1131     /* Write read image procedure */
1132     os << "<<\n";
1133     os << "  /ImageType 3\n";
1134     os << "  /InterleaveType 1\n";
1136     os << "  /MaskDict\n";
1137     os << "  <<\n";
1138     os << "    /ImageType 1\n";
1139     os << "    /Width " << width << "\n";
1140     os << "    /Height " << height << "\n";
1141     os << "    /ImageMatrix "
1142        << "[" << width << " "
1143        << 0 << " "
1144        << 0 << " "
1145        << -((long) height) << " "
1146        << 0 << " "
1147        << height << "]\n";
1148     os << "    /BitsPerComponent 8\n";
1149     os << "    /Decode [1 0]\n";
1150     os << "  >>\n";
1152     os << "  /DataDict\n";
1153     os << "  <<\n";
1154     os << "    /ImageType 1\n";
1155     os << "    /Width " << width << "\n";
1156     os << "    /Height " << height << "\n";
1157     os << "    /ImageMatrix "
1158        << "[" << width << " "
1159        << 0 << " "
1160        << 0 << " "
1161        << -((long )height) << " "
1162        << 0 << " "
1163        << height << "]\n";
1164     os << "    /DataSource currentfile /ASCII85Decode filter\n";
1165     os << "    /BitsPerComponent 8\n";
1166     os << "    /Decode [0 1 0 1 0 1]\n";
1167     os << "  >>\n";
1169     os << ">>\n";
1171     /* Allocate buffer for packbits data. Worst case: Less than 1% increase */
1172     guchar *const packb = (guchar *)g_malloc((4*width * 105)/100+2);
1173     guchar *const plane = (guchar *)g_malloc(4*width);
1175     os << "image\n";
1177     ascii85_init();
1178     
1179     for (unsigned i = 0; i < height; i++) {
1180         guchar const *const src = px + i * rs;
1182         guchar const *src_ptr = src;
1183         guchar *plane_ptr = plane;
1184         for (unsigned j = 0; j < width; j++) {
1185             *(plane_ptr++) = *(src_ptr+3);
1186             *(plane_ptr++) = *(src_ptr+0);
1187             *(plane_ptr++) = *(src_ptr+1);
1188             *(plane_ptr++) = *(src_ptr+2);
1189             src_ptr += 4;
1190         }
1191         
1192         ascii85_nout(4*width, plane, os);
1193     }
1194     ascii85_done(os);
1196     g_free(packb);
1197     g_free(plane);
1199     os << "grestore\n";
1201     fprintf(ofp, "%s", os.str().c_str());
1203     return 0;
1206 bool
1207 PrintPS::textToPath(Inkscape::Extension::Print * ext)
1209     return ext->get_param_bool("textToPath");
1212 #include "clear-n_.h"
1214 void
1215 PrintPS::init(void)
1217     /* SVG in */
1218     (void) Inkscape::Extension::build_from_mem(
1219         "<inkscape-extension>\n"
1220         "<name>" N_("Postscript Print") "</name>\n"
1221         "<id>" SP_MODULE_KEY_PRINT_PS "</id>\n"
1222         "<param name=\"bitmap\" type=\"boolean\">FALSE</param>\n"
1223         "<param name=\"resolution\" type=\"string\">72</param>\n"
1224         "<param name=\"destination\" type=\"string\">| lp</param>\n"
1225         "<param name=\"pageBoundingBox\" type=\"boolean\">TRUE</param>\n"
1226         "<param name=\"textToPath\" type=\"boolean\">TRUE</param>\n"
1227         "<print/>\n"
1228         "</inkscape-extension>", new PrintPS());
1232 }  /* namespace Internal */
1233 }  /* namespace Extension */
1234 }  /* namespace Inkscape */
1236 /* End of GNU GPL code */
1239 /*
1240   Local Variables:
1241   mode:c++
1242   c-file-style:"stroustrup"
1243   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1244   indent-tabs-mode:nil
1245   fill-column:99
1246   End:
1247 */
1248 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :