Code

r13108@tres: ted | 2006-07-30 12:27:51 -0700
[inkscape.git] / src / extension / internal / pdf.cpp
1 #define __SP_PDF_C__\r
2 \r
3 /** \file\r
4  * PDF printing.\r
5  */\r
6 /*\r
7  * Authors:\r
8  *   Lauris Kaplinski <lauris@kaplinski.com>\r
9  *   bulia byak <buliabyak@users.sf.net>\r
10  *   Ulf Erikson <ulferikson@users.sf.net>\r
11  *\r
12  * Basic printing code, EXCEPT image and\r
13  * ascii85 filter is in public domain\r
14  *\r
15  * Image printing and Ascii85 filter:\r
16  *\r
17  * Copyright (C) 1995 Spencer Kimball and Peter Mattis\r
18  * Copyright (C) 1997-98 Peter Kirchgessner\r
19  * George White <aa056@chebucto.ns.ca>\r
20  * Austin Donnelly <austin@gimp.org>\r
21  *\r
22  * Licensed under GNU GPL\r
23  */\r
24 \r
25 /* Plain Print */\r
26 \r
27 #ifdef HAVE_CONFIG_H\r
28 # include "config.h"\r
29 #endif\r
30 \r
31 #include <signal.h>\r
32 #include <errno.h>\r
33 \r
34 #include <glib/gmem.h>\r
35 #include <libnr/n-art-bpath.h>\r
36 #include <libnr/nr-point-matrix-ops.h>\r
37 \r
38 #include <gtk/gtkstock.h>\r
39 #include <gtk/gtkvbox.h>\r
40 #include <gtk/gtkframe.h>\r
41 #include <gtk/gtkradiobutton.h>\r
42 #include <gtk/gtkcombo.h>\r
43 #include <gtk/gtklabel.h>\r
44 #include <gtk/gtkentry.h>\r
45 #include <gtk/gtktooltips.h>\r
46 \r
47 #include <glibmm/i18n.h>\r
48 #include "display/nr-arena-item.h"\r
49 #include "display/canvas-bpath.h"\r
50 #include "sp-item.h"\r
51 #include "style.h"\r
52 #include "sp-linear-gradient.h"\r
53 #include "sp-radial-gradient.h"\r
54 \r
55 #include "libnrtype/font-instance.h"\r
56 #include "libnrtype/font-style-to-pos.h"\r
57 \r
58 #include <unit-constants.h>\r
59 \r
60 #include "pdf.h"\r
61 #include "extension/system.h"\r
62 #include "extension/print.h"\r
63 \r
64 #include "io/sys.h"\r
65 \r
66 #include "pdf-mini.h"\r
67 \r
68 namespace Inkscape {\r
69 namespace Extension {\r
70 namespace Internal {\r
71 \r
72 PrintPDF::PrintPDF() :\r
73     _stream(NULL),\r
74     _dpi(72),\r
75     _bitmap(false)\r
76 {\r
77     _num_alphas = 10;\r
78     _pushed_alphas = (float*) malloc(_num_alphas*sizeof(float));\r
79 }\r
80 \r
81 PrintPDF::~PrintPDF(void)\r
82 {\r
83     free(_pushed_alphas);\r
84 \r
85     /* fixme: should really use pclose for popen'd streams */\r
86     if (_stream) fclose(_stream);\r
87 \r
88     /* restore default signal handling for SIGPIPE */\r
89 #if !defined(_WIN32) && !defined(__WIN32__)\r
90     (void) signal(SIGPIPE, SIG_DFL);\r
91 #endif\r
92 \r
93     return;\r
94 }\r
95 \r
96 unsigned int\r
97 PrintPDF::setup(Inkscape::Extension::Print * mod)\r
98 {\r
99     static gchar const *const pdr[] = {"72", "75", "100", "144", "150", "200", "300", "360", "600", "1200", "2400", NULL};\r
100 \r
101 #ifdef TED\r
102     Inkscape::XML::Node *repr = ((SPModule *) mod)->repr;\r
103 #endif\r
104 \r
105     unsigned int ret = FALSE;\r
106 \r
107     /* Create dialog */\r
108     GtkTooltips *tt = gtk_tooltips_new();\r
109     g_object_ref((GObject *) tt);\r
110     gtk_object_sink((GtkObject *) tt);\r
111 \r
112     GtkWidget *dlg = gtk_dialog_new_with_buttons(_("Print Destination"),\r
113 //            SP_DT_WIDGET(SP_ACTIVE_DESKTOP)->window,\r
114             NULL,\r
115             (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT),\r
116             GTK_STOCK_CANCEL,\r
117             GTK_RESPONSE_CANCEL,\r
118             GTK_STOCK_PRINT,\r
119             GTK_RESPONSE_OK,\r
120             NULL);\r
121 \r
122     gtk_dialog_set_default_response(GTK_DIALOG(dlg), GTK_RESPONSE_OK);\r
123 \r
124     GtkWidget *vbox = GTK_DIALOG(dlg)->vbox;\r
125     gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);\r
126     /* Print properties frame */\r
127     GtkWidget *f = gtk_frame_new(_("Print properties"));\r
128     gtk_box_pack_start(GTK_BOX(vbox), f, FALSE, FALSE, 4);\r
129     GtkWidget *vb = gtk_vbox_new(FALSE, 4);\r
130     gtk_container_add(GTK_CONTAINER(f), vb);\r
131     gtk_container_set_border_width(GTK_CONTAINER(vb), 4);\r
132     /* Print type */\r
133     bool const p2bm = mod->get_param_bool("bitmap");\r
134     GtkWidget *rb = gtk_radio_button_new_with_label(NULL, _("Print using PDF operators"));\r
135     gtk_tooltips_set_tip((GtkTooltips *) tt, rb,\r
136                          _("Use PDF vector operators. The resulting image is usually smaller "\r
137                            "in file size and can be arbitrarily scaled, but "\r
138                            "patterns will be lost."), NULL);\r
139     if (!p2bm) gtk_toggle_button_set_active((GtkToggleButton *) rb, TRUE);\r
140     gtk_box_pack_start(GTK_BOX(vb), rb, FALSE, FALSE, 0);\r
141     rb = gtk_radio_button_new_with_label(gtk_radio_button_get_group((GtkRadioButton *) rb), _("Print as bitmap"));\r
142     gtk_tooltips_set_tip((GtkTooltips *) tt, rb,\r
143                          _("Print everything as bitmap. The resulting image is usually larger "\r
144                            "in file size and cannot be arbitrarily scaled without quality loss, "\r
145                            "but all objects will be rendered exactly as displayed."), NULL);\r
146     if (p2bm) gtk_toggle_button_set_active((GtkToggleButton *) rb, TRUE);\r
147     gtk_box_pack_start(GTK_BOX(vb), rb, FALSE, FALSE, 0);\r
148     /* Resolution */\r
149     GtkWidget *hb = gtk_hbox_new(FALSE, 4);\r
150     gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, 0);\r
151     GtkWidget *combo = gtk_combo_new();\r
152     gtk_combo_set_value_in_list(GTK_COMBO(combo), FALSE, FALSE);\r
153     gtk_combo_set_use_arrows(GTK_COMBO(combo), TRUE);\r
154     gtk_combo_set_use_arrows_always(GTK_COMBO(combo), TRUE);\r
155     gtk_widget_set_size_request(combo, 64, -1);\r
156     gtk_tooltips_set_tip((GtkTooltips *) tt, GTK_COMBO(combo)->entry,\r
157                          _("Preferred resolution (dots per inch) of bitmap"), NULL);\r
158     /* Setup strings */\r
159     GList *sl = NULL;\r
160     for (unsigned i = 0; pdr[i] != NULL; i++) {\r
161         sl = g_list_prepend(sl, (gpointer) pdr[i]);\r
162     }\r
163     sl = g_list_reverse(sl);\r
164     gtk_combo_set_popdown_strings(GTK_COMBO(combo), sl);\r
165     g_list_free(sl);\r
166     if (1) {\r
167         gchar const *val = mod->get_param_string("resolution");\r
168         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), val);\r
169     }\r
170     gtk_box_pack_end(GTK_BOX(hb), combo, FALSE, FALSE, 0);\r
171     GtkWidget *l = gtk_label_new(_("Resolution:"));\r
172     gtk_box_pack_end(GTK_BOX(hb), l, FALSE, FALSE, 0);\r
173 \r
174     /* Print destination frame */\r
175     f = gtk_frame_new(_("Print destination"));\r
176     gtk_box_pack_start(GTK_BOX(vbox), f, FALSE, FALSE, 4);\r
177     vb = gtk_vbox_new(FALSE, 4);\r
178     gtk_container_add(GTK_CONTAINER(f), vb);\r
179     gtk_container_set_border_width(GTK_CONTAINER(vb), 4);\r
180 \r
181     l = gtk_label_new(_("Printer name (as given by lpstat -p);\n"\r
182                         "leave empty to use the system default printer.\n"\r
183                         "Use '> filename' to print to file.\n"\r
184                         "Use '| prog arg...' to pipe to a program."));\r
185     gtk_box_pack_start(GTK_BOX(vb), l, FALSE, FALSE, 0);\r
186 \r
187     GtkWidget *e = gtk_entry_new();\r
188     if (1) {\r
189         gchar const *val = mod->get_param_string("destination");\r
190         gtk_entry_set_text(GTK_ENTRY(e), ( val != NULL\r
191                                            ? val\r
192                                            : "" ));\r
193     }\r
194     gtk_box_pack_start(GTK_BOX(vb), e, FALSE, FALSE, 0);\r
195 \r
196     // pressing enter in the destination field is the same as clicking Print:\r
197     gtk_entry_set_activates_default(GTK_ENTRY(e), TRUE);\r
198 \r
199     gtk_widget_show_all(vbox);\r
200 \r
201     int const response = gtk_dialog_run(GTK_DIALOG(dlg));\r
202 \r
203     g_object_unref((GObject *) tt);\r
204 \r
205     if (response == GTK_RESPONSE_OK) {\r
206         gchar const *fn;\r
207         char const *sstr;\r
208 \r
209         _bitmap = gtk_toggle_button_get_active((GtkToggleButton *) rb);\r
210         sstr = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)->entry));\r
211         _dpi = (unsigned int) MAX((int)(atof(sstr)), 1);\r
212         /* Arrgh, have to do something */\r
213         fn = gtk_entry_get_text(GTK_ENTRY(e));\r
214         /* skip leading whitespace, bug #1068483 */\r
215         while (fn && *fn==' ') { fn++; }\r
216         /* g_print("Printing to %s\n", fn); */\r
217 \r
218         mod->set_param_bool("bitmap", _bitmap);\r
219         mod->set_param_string("resolution", (gchar *)sstr);\r
220         mod->set_param_string("destination", (gchar *)fn);\r
221         ret = TRUE;\r
222     }\r
223 \r
224     gtk_widget_destroy(dlg);\r
225 \r
226     return ret;\r
227 }\r
228 \r
229 unsigned int\r
230 PrintPDF::begin(Inkscape::Extension::Print *mod, SPDocument *doc)\r
231 {\r
232     gboolean epsexport = false;\r
233 \r
234     _latin1_encoded_fonts.clear();\r
235     _newlatin1font_proc_defined = false;\r
236 \r
237     FILE *osf = NULL;\r
238     FILE *osp = NULL;\r
239 \r
240     _curr_alpha = 0;\r
241     _pushed_alphas[_curr_alpha] = 1.0;\r
242 \r
243     gsize bytesRead = 0;\r
244     gsize bytesWritten = 0;\r
245     GError *error = NULL;\r
246     gchar const *utf8_fn = mod->get_param_string("destination");\r
247     gchar *local_fn = g_filename_from_utf8( utf8_fn,\r
248                                             -1,  &bytesRead,  &bytesWritten, &error);\r
249     gchar const *fn = local_fn;\r
250 \r
251     /* TODO: Replace the below fprintf's with something that does the right thing whether in\r
252      * gui or batch mode (e.g. --print=blah).  Consider throwing an exception: currently one of\r
253      * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the\r
254      * return code.\r
255      */\r
256     if (fn != NULL) {\r
257         if (*fn == '|') {\r
258             fn += 1;\r
259             while (isspace(*fn)) fn += 1;\r
260 #ifndef WIN32\r
261             osp = popen(fn, "wb");\r
262 #else\r
263             osp = _popen(fn, "wb");\r
264 #endif\r
265             if (!osp) {\r
266                 fprintf(stderr, "inkscape: popen(%s): %s\n",\r
267                         fn, strerror(errno));\r
268                 return 0;\r
269             }\r
270             _stream = osp;\r
271         } else if (*fn == '>') {\r
272             fn += 1;\r
273             epsexport = g_str_has_suffix(fn,".eps");\r
274             while (isspace(*fn)) fn += 1;\r
275             Inkscape::IO::dump_fopen_call(fn, "K");\r
276             osf = Inkscape::IO::fopen_utf8name(fn, "wb+");\r
277             if (!osf) {\r
278                 fprintf(stderr, "inkscape: fopen(%s): %s\n",\r
279                         fn, strerror(errno));\r
280                 return 0;\r
281             }\r
282             _stream = osf;\r
283         } else {\r
284             /* put cwd stuff in here */\r
285             gchar *qn = ( *fn\r
286                           ? g_strdup_printf("lpr -P %s", fn)  /* FIXME: quote fn */\r
287                           : g_strdup("lpr") );\r
288 #ifndef WIN32\r
289             osp = popen(qn, "wb");\r
290 #else\r
291             osp = _popen(qn, "wb");\r
292 #endif\r
293             if (!osp) {\r
294                 fprintf(stderr, "inkscape: popen(%s): %s\n",\r
295                         qn, strerror(errno));\r
296                 return 0;\r
297             }\r
298             g_free(qn);\r
299             _stream = osp;\r
300         }\r
301     }\r
302 \r
303     g_free(local_fn);\r
304 \r
305     if (_stream) {\r
306         /* fixme: this is kinda icky */\r
307 #if !defined(_WIN32) && !defined(__WIN32__)\r
308         (void) signal(SIGPIPE, SIG_IGN);\r
309 #endif\r
310     }\r
311 \r
312     pdf_file = new PdfFile(_stream);\r
313     doc_info = pdf_file->begin_document(1.4);\r
314     *doc_info << "  /Title(" << doc->name << ")\n";\r
315     *doc_info << "  /Author(" << g_get_real_name()  << ")\n";\r
316 //    *doc_info << "  /Subject(" << "?" << ")\n";\r
317 //    *doc_info << "  /Keywords(" << "?" << ")\n";\r
318     *doc_info << "  /Creator(" << "www.inkscape.org" << ")\n";\r
319     *doc_info << "  /Producer(" << "Inkscape " << PACKAGE_STRING << ")\n";\r
320         //the date should be in ISO/IEC 8824\r
321         GDate date;\r
322         GTimeVal ltime;\r
323         glong time_hh, time_mm, time_ss;\r
324 #if GLIB_CHECK_VERSION(2,9,0)\r
325         g_date_set_time_t (&date, time (NULL));\r
326 #else\r
327         g_date_set_time(&date, time (NULL));\r
328 #endif\r
329         gchar date_str[100], time_str[100];\r
330         g_date_strftime(date_str, 99, "%Y%m%d", &date);\r
331         g_get_current_time(&ltime);\r
332         time_hh=(ltime.tv_sec/3600)%24;\r
333         time_mm=(ltime.tv_sec/60)%60;\r
334         time_ss=(ltime.tv_sec)%60;\r
335         g_snprintf(time_str, 99, "%02ld%02ld%02ld", time_hh, time_mm, time_ss); \r
336         *doc_info << "  /CreationDate(D:" << date_str << time_str << "Z)\n";\r
337 //      *doc_info << "  /CreationDate(D:" << date_str << time_str << "OHH'mm')\n";\r
338 \r
339     /* flush this to test output stream as early as possible */\r
340     if (fflush(_stream)) {\r
341         /*g_print("caught error in sp_module_print_plain_begin\n");*/\r
342         if (ferror(_stream)) {\r
343             g_print("Error %d on output stream: %s\n", errno,\r
344                     g_strerror(errno));\r
345         }\r
346         g_print("Printing failed\n");\r
347         /* fixme: should use pclose() for pipes */\r
348         fclose(_stream);\r
349         _stream = NULL;\r
350         fflush(stdout);\r
351         return 0;\r
352     }\r
353 \r
354     // width and height in pt\r
355     _width = sp_document_width(doc) * PT_PER_PX;\r
356     _height = sp_document_height(doc) * PT_PER_PX;\r
357 \r
358     NRRect d;\r
359     bool   pageBoundingBox;\r
360     pageBoundingBox = mod->get_param_bool("pageBoundingBox");\r
361     // printf("Page Bounding Box: %s\n", pageBoundingBox ? "TRUE" : "FALSE");\r
362     if (pageBoundingBox) {\r
363         d.x0 = d.y0 = 0;\r
364         d.x1 = ceil(_width);\r
365         d.y1 = ceil(_height);\r
366     } else {\r
367         SPItem* doc_item = SP_ITEM(sp_document_root(doc));\r
368         sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE);\r
369         // convert from px to pt\r
370         d.x0 *= PT_PER_PX;\r
371         d.x1 *= PT_PER_PX;\r
372         d.y0 *= PT_PER_PX;\r
373         d.y1 *= PT_PER_PX;\r
374     }\r
375 \r
376     page_stream = pdf_file->begin_page( (int) d.x0, (int) d.y0,\r
377                                         (int) d.x1, (int) d.y1 );\r
378 \r
379     if (!_bitmap) {\r
380         *page_stream << PT_PER_PX << " 0 0 "\r
381                      << -PT_PER_PX << " 0 " << (int) ceil(_height)\r
382                      << " cm\n";\r
383         // from now on we can output px, but they will be treated as pt\r
384     }\r
385     \r
386     return 1;\r
387 }\r
388 \r
389 unsigned int\r
390 PrintPDF::finish(Inkscape::Extension::Print *mod)\r
391 {\r
392     if (!_stream) return 0;\r
393 \r
394     if (_bitmap) {\r
395         double const dots_per_pt = _dpi / PT_PER_IN;\r
396 \r
397         double const x0 = 0.0;\r
398         double const y0 = 0.0;\r
399         double const x1 = x0 + _width;\r
400         double const y1 = y0 + _height;\r
401 \r
402         /* Bitmap width/height in bitmap dots. */\r
403         int const width = (int) (_width * dots_per_pt + 0.5);\r
404         int const height = (int) (_height * dots_per_pt + 0.5);\r
405 \r
406         NRMatrix affine;\r
407         affine.c[0] = width / ((x1 - x0) * PX_PER_PT);\r
408         affine.c[1] = 0.0;\r
409         affine.c[2] = 0.0;\r
410         affine.c[3] = height / ((y1 - y0) * PX_PER_PT);\r
411         affine.c[4] = -affine.c[0] * x0;\r
412         affine.c[5] = -affine.c[3] * y0;\r
413 \r
414         nr_arena_item_set_transform(mod->root, &affine);\r
415 \r
416         guchar *const px = g_new(guchar, 4 * width * 64);\r
417 \r
418         for (int y = 0; y < height; y += 64) {\r
419             /* Set area of interest. */\r
420             NRRectL bbox;\r
421             bbox.x0 = 0;\r
422             bbox.y0 = y;\r
423             bbox.x1 = width;\r
424             bbox.y1 = MIN(height, y + 64);\r
425 \r
426             /* Update to renderable state. */\r
427             NRGC gc(NULL);\r
428             nr_matrix_set_identity(&gc.transform);\r
429             nr_arena_item_invoke_update(mod->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);\r
430             /* Render */\r
431             /* This should take guchar* instead of unsigned char*) */\r
432             NRPixBlock pb;\r
433             nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N,\r
434                                      bbox.x0, bbox.y0, bbox.x1, bbox.y1,\r
435                                      (guchar*)px, 4 * width, FALSE, FALSE);\r
436             memset(px, 0xff, 4 * width * 64);\r
437             nr_arena_item_invoke_render(mod->root, &bbox, &pb, 0);\r
438             /* Blitter goes here */\r
439             NRMatrix imgt;\r
440             imgt.c[0] = (bbox.x1 - bbox.x0) / dots_per_pt;\r
441             imgt.c[1] = 0.0;\r
442             imgt.c[2] = 0.0;\r
443             imgt.c[3] = (bbox.y1 - bbox.y0) / dots_per_pt;\r
444             imgt.c[4] = 0.0;\r
445             imgt.c[5] = _height - y / dots_per_pt - (bbox.y1 - bbox.y0) / dots_per_pt;\r
446 \r
447             print_image(_stream, px, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, 4 * width, &imgt);\r
448         }\r
449 \r
450         g_free(px);\r
451     }\r
452 \r
453     pdf_file->end_page(page_stream);\r
454     pdf_file->end_document(doc_info);\r
455 \r
456     delete pdf_file;\r
457 \r
458     /* Flush stream to be sure. */\r
459     (void) fflush(_stream);\r
460 \r
461     /* fixme: should really use pclose for popen'd streams */\r
462     fclose(_stream);\r
463     _stream = 0;\r
464     _latin1_encoded_fonts.clear();\r
465 \r
466     return 0;\r
467 }\r
468 \r
469 unsigned int\r
470 PrintPDF::bind(Inkscape::Extension::Print *mod, NRMatrix const *transform, float opacity)\r
471 {\r
472     if (!_stream) return 0;  // XXX: fixme, returning -1 as unsigned.\r
473     if (_bitmap) return 0;\r
474 \r
475     *page_stream << "q\n";\r
476     *page_stream << transform->c[0] << " "\r
477                  << transform->c[1] << " "\r
478                  << transform->c[2] << " "\r
479                  << transform->c[3] << " "\r
480                  << transform->c[4] << " "\r
481                  << transform->c[5] << " cm\n";\r
482 \r
483     float alpha = opacity * _pushed_alphas[_curr_alpha];\r
484         \r
485     _curr_alpha++;\r
486     if (_curr_alpha >= _num_alphas) {\r
487         _num_alphas = _num_alphas*2;\r
488         _pushed_alphas = (float *) realloc(_pushed_alphas, _num_alphas*sizeof(float));\r
489     }\r
490     _pushed_alphas[_curr_alpha] = alpha;\r
491 \r
492     return 1;\r
493 }\r
494 \r
495 unsigned int\r
496 PrintPDF::release(Inkscape::Extension::Print *mod)\r
497 {\r
498     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
499     if (_bitmap) return 0;\r
500 \r
501     if (_curr_alpha > 0)\r
502         _curr_alpha--;\r
503 \r
504     *page_stream << "Q\n";\r
505 \r
506     return 1;\r
507 }\r
508 \r
509 unsigned int\r
510 PrintPDF::comment(Inkscape::Extension::Print *mod, char const *comment)\r
511 {\r
512     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
513     if (_bitmap) return 0;\r
514 \r
515     *page_stream << "% " << comment;\r
516  \r
517     return 1;\r
518 }\r
519 \r
520 void\r
521 PrintPDF::print_fill_alpha(SVGOStringStream &/*os*/, SPStyle const *const style, NRRect const *pbox)\r
522 {\r
523     g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR\r
524                       || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
525                            && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) );\r
526     \r
527     if (style->fill.type == SP_PAINT_TYPE_COLOR) {\r
528         float alpha = 1.0;\r
529         alpha *= SP_SCALE24_TO_FLOAT(style->fill_opacity.value);\r
530         alpha *= _pushed_alphas[_curr_alpha];\r
531 \r
532         if (alpha != 1.0) {\r
533             PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_extgstate);\r
534             *pdf_alpha << "<< /Type /ExtGState\n";\r
535             *pdf_alpha << "   /ca " << alpha << "\n";\r
536             *pdf_alpha << "   /AIS false\n";\r
537             *pdf_alpha << ">>\n";\r
538             \r
539             *page_stream << pdf_alpha->get_name()\r
540                          << " gs\n";\r
541 \r
542             pdf_file->end_resource(pdf_alpha);\r
543         }\r
544     } else {\r
545         g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
546                   && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
547         \r
548         if (SP_IS_LINEARGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
549             \r
550             SPLinearGradient *lg=SP_LINEARGRADIENT(SP_STYLE_FILL_SERVER (style));\r
551 \r
552             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built\r
553 \r
554             NR::Point p1 (lg->x1.computed, lg->y1.computed);\r
555             NR::Point p2 (lg->x2.computed, lg->y2.computed);\r
556             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
557                 // convert to userspace\r
558                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
559                 p1 *= bbox2user;\r
560                 p2 *= bbox2user;\r
561             }\r
562             \r
563             double alpha = 1.0;\r
564             for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {\r
565                 alpha *= lg->vector.stops[i].opacity;\r
566             }\r
567             \r
568             if (alpha != 1.0 || _pushed_alphas[_curr_alpha] != 1.0) {\r
569                 PdfObject *pdf_gstate = pdf_file->begin_resource(pdf_extgstate);\r
570                 *pdf_gstate << "<< /Type /ExtGState\n";\r
571                 \r
572                 if (_pushed_alphas[_curr_alpha] != 1.0) {\r
573                     *pdf_gstate << "   /ca " << _pushed_alphas[_curr_alpha] << "\n";\r
574                     *pdf_gstate << "   /AIS false\n";\r
575                 }\r
576                 \r
577                 if (alpha != 1.0) {\r
578                     PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_shading);\r
579                     PdfObject *pdf_smask = pdf_file->begin_resource(pdf_none);\r
580                     PdfObject *pdf_xobj = pdf_file->begin_resource(pdf_none);\r
581 \r
582 \r
583                     *pdf_gstate << "   /SMask " << pdf_smask->get_id() << " 0 R\n";\r
584 \r
585             \r
586                     *pdf_alpha << "<<\n/ShadingType 2\n/ColorSpace /DeviceGray\n";\r
587                     *pdf_alpha << "/Coords [" << p1[NR::X] << " " << p1[NR::Y] << " " << p2[NR::X] << " " << p2[NR::Y] <<"]\n";\r
588                     *pdf_alpha << "/Extend [true true]\n";\r
589                     *pdf_alpha << "/Domain [0 1]\n";\r
590                     *pdf_alpha << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
591 \r
592                     for (gint i = 0; unsigned(i) < lg->vector.stops.size() - 1; i++) {\r
593                         *pdf_alpha << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
594                         *pdf_alpha << "/C0 [" << lg->vector.stops[i].opacity << "]\n";\r
595                         *pdf_alpha << "/C1 [" << lg->vector.stops[i+1].opacity << "]\n";\r
596                         *pdf_alpha << "/N 1\n>>\n";\r
597                     }\r
598                     *pdf_alpha << "]\n/Domain [0 1]\n";\r
599                     *pdf_alpha << "/Bounds [ ";\r
600                     for (gint i=0;unsigned(i)<lg->vector.stops.size()-2;i++) {\r
601                         *pdf_alpha << lg->vector.stops[i+1].offset <<" ";\r
602                     }\r
603                     *pdf_alpha << "]\n";\r
604                     *pdf_alpha << "/Encode [ ";\r
605                     for (gint i=0;unsigned(i)<lg->vector.stops.size()-1;i++) {\r
606                         *pdf_alpha << "0 1 ";\r
607                     }\r
608                     *pdf_alpha << "]\n";\r
609                     *pdf_alpha << ">>\n>>\n";\r
610 \r
611                     \r
612                     *pdf_smask << "<< /Type /Mask\n";\r
613                     *pdf_smask << "   /S /Alpha\n";\r
614                     *pdf_smask << "   /BC [0.0]\n";\r
615                     *pdf_smask << "   /G " << pdf_xobj->get_id() << " 0 R\n";\r
616                     *pdf_smask << ">>\n";\r
617 \r
618                     \r
619                     *pdf_xobj << "<< /Type /XObject\n";\r
620                     *pdf_xobj << "   /Subtype /Form\n";\r
621                     *pdf_xobj << "   /FormType 1\n";\r
622                     *pdf_xobj << "   /BBox ["\r
623                               << pbox->x0 << " "\r
624                               << pbox->y0 << " "\r
625                               << pbox->x1 << " "\r
626                               << pbox->y1 << "]\n";\r
627                     *pdf_xobj << "   /Group\n";\r
628                     *pdf_xobj << "   << /Type /Group\n";\r
629                     *pdf_xobj << "      /S /Transparency\n";\r
630                     *pdf_xobj << "      /CS /DeviceGray \n";\r
631                     *pdf_xobj << "   >>\n";\r
632                     \r
633                     Inkscape::SVGOStringStream os;\r
634                     os.setf(std::ios::fixed);\r
635                     \r
636                     os << "q\n"\r
637                        << pbox->x0 << " " << pbox->y0 << " m\n"\r
638                        << pbox->x1 << " " << pbox->y0 << " l\n"\r
639                        << pbox->x1 << " " << pbox->y1 << " l\n"\r
640                        << pbox->x0 << " " << pbox->y1 << " l\n"\r
641                        << pbox->x0 << " " << pbox->y0 << " l\n"\r
642                        << "h\n"\r
643                        << "W* n\n";\r
644                     \r
645                     SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
646                     if (g->gradientTransform_set) {\r
647                         os << g->gradientTransform[0] << " "\r
648                            << g->gradientTransform[1] << " "\r
649                            << g->gradientTransform[2] << " "\r
650                            << g->gradientTransform[3] << " "\r
651                            << g->gradientTransform[4] << " "\r
652                            << g->gradientTransform[5] << " cm\n"; \r
653                     }\r
654                     \r
655                     os << pdf_alpha->get_name() << " sh\n";\r
656                     os << "Q\n";\r
657                     *pdf_xobj << "   /Length " << strlen(os.str().c_str()) << "\n";\r
658                     *pdf_xobj << ">>\n";\r
659                     *pdf_xobj << "stream\n";\r
660                     *pdf_xobj << os.str().c_str();\r
661                     *pdf_xobj << "endstream\n";\r
662 \r
663 \r
664                     pdf_file->end_resource(pdf_alpha);\r
665                     pdf_file->end_resource(pdf_smask);\r
666                     pdf_file->end_resource(pdf_xobj);\r
667                 }\r
668                 \r
669                 *pdf_gstate << ">>\n";\r
670 \r
671                 *page_stream << pdf_gstate->get_name() << " gs\n";\r
672                     \r
673                 pdf_file->end_resource(pdf_gstate);\r
674             }\r
675         } else if (SP_IS_RADIALGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
676 \r
677             SPRadialGradient *rg=SP_RADIALGRADIENT(SP_STYLE_FILL_SERVER (style));\r
678 \r
679             sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built\r
680 \r
681             NR::Point c (rg->cx.computed, rg->cy.computed);\r
682             NR::Point f (rg->fx.computed, rg->fy.computed);\r
683             double r = rg->r.computed;\r
684             if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
685                 // convert to userspace\r
686                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
687                 c *= bbox2user;\r
688                 f *= bbox2user;\r
689                 r *= bbox2user.expansion();\r
690             }\r
691             \r
692             double alpha = 1.0;\r
693             for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {\r
694                 alpha *= rg->vector.stops[i].opacity;\r
695             }\r
696 \r
697             if (alpha != 1.0 || _pushed_alphas[_curr_alpha] != 1.0) {\r
698                 PdfObject *pdf_gstate = pdf_file->begin_resource(pdf_extgstate);\r
699                 *pdf_gstate << "<< /Type /ExtGState\n";\r
700                 \r
701                 if (_pushed_alphas[_curr_alpha] != 1.0) {\r
702                     *pdf_gstate << "   /ca " << _pushed_alphas[_curr_alpha] << "\n";\r
703                     *pdf_gstate << "   /AIS false\n";\r
704                 }\r
705                 \r
706                 if (alpha != 1.0) {\r
707                     PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_shading);\r
708                     PdfObject *pdf_smask = pdf_file->begin_resource(pdf_none);\r
709                     PdfObject *pdf_xobj = pdf_file->begin_resource(pdf_none);\r
710 \r
711 \r
712                     *pdf_gstate << "   /SMask " << pdf_smask->get_id() << " 0 R\n";\r
713 \r
714 \r
715                     *pdf_alpha << "<<\n/ShadingType 3\n/ColorSpace /DeviceGray\n";\r
716                     *pdf_alpha << "/Coords ["<< f[NR::X] <<" "<< f[NR::Y] <<" 0 "<< c[NR::X] <<" "<< c[NR::Y] <<" "<< r <<"]\n";\r
717                     *pdf_alpha << "/Extend [true true]\n";\r
718                     *pdf_alpha << "/Domain [0 1]\n";\r
719                     *pdf_alpha << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
720 \r
721                     for (gint i = 0; unsigned(i) < rg->vector.stops.size() - 1; i++) {\r
722                         *pdf_alpha << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
723                         *pdf_alpha << "/C0 [" << rg->vector.stops[i].opacity << "]\n";\r
724                         *pdf_alpha << "/C1 [" << rg->vector.stops[i+1].opacity << "]\n";\r
725                         *pdf_alpha << "/N 1\n>>\n";\r
726                     }\r
727                     *pdf_alpha << "]\n/Domain [0 1]\n";\r
728                     *pdf_alpha << "/Bounds [ ";\r
729 \r
730                     for (gint i=0;unsigned(i)<rg->vector.stops.size()-2;i++) {\r
731                         *pdf_alpha << rg->vector.stops[i+1].offset <<" ";\r
732                     }\r
733                     *pdf_alpha << "]\n";\r
734                     *pdf_alpha << "/Encode [ ";\r
735                     for (gint i=0;unsigned(i)<rg->vector.stops.size()-1;i++) {\r
736                         *pdf_alpha << "0 1 ";\r
737                     }\r
738                     *pdf_alpha << "]\n";\r
739                     *pdf_alpha << ">>\n>>\n";\r
740                     \r
741                     \r
742                     *pdf_smask << "<< /Type /Mask\n";\r
743                     *pdf_smask << "   /S /Alpha\n";\r
744                     *pdf_smask << "   /BC [0.0]\n";\r
745                     *pdf_smask << "   /G " << pdf_xobj->get_id() << " 0 R\n";\r
746                     *pdf_smask << ">>\n";\r
747 \r
748                     \r
749                     *pdf_xobj << "<< /Type /XObject\n";\r
750                     *pdf_xobj << "   /Subtype /Form\n";\r
751                     *pdf_xobj << "   /FormType 1\n";\r
752                     *pdf_xobj << "   /BBox ["\r
753                               << pbox->x0 << " "\r
754                               << pbox->y0 << " "\r
755                               << pbox->x1 << " "\r
756                               << pbox->y1 << "]\n";\r
757                     *pdf_xobj << "   /Group\n";\r
758                     *pdf_xobj << "   << /Type /Group\n";\r
759                     *pdf_xobj << "      /S /Transparency\n";\r
760                     *pdf_xobj << "      /CS /DeviceGray \n";\r
761                     *pdf_xobj << "   >>\n";\r
762                     \r
763                     Inkscape::SVGOStringStream os;\r
764                     os.setf(std::ios::fixed);\r
765                     \r
766                     os << "q\n"\r
767                        << pbox->x0 << " " << pbox->y0 << " m\n"\r
768                        << pbox->x1 << " " << pbox->y0 << " l\n"\r
769                        << pbox->x1 << " " << pbox->y1 << " l\n"\r
770                        << pbox->x0 << " " << pbox->y1 << " l\n"\r
771                        << pbox->x0 << " " << pbox->y0 << " l\n"\r
772                        << "h\n"\r
773                        << "W* n\n";\r
774                     \r
775                     SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
776                     if (g->gradientTransform_set) {\r
777                         os << g->gradientTransform[0] << " "\r
778                            << g->gradientTransform[1] << " "\r
779                            << g->gradientTransform[2] << " "\r
780                            << g->gradientTransform[3] << " "\r
781                            << g->gradientTransform[4] << " "\r
782                            << g->gradientTransform[5] << " cm\n"; \r
783                     }\r
784                     \r
785                     os << pdf_alpha->get_name() << " sh\n";\r
786                     os << "Q\n";\r
787                     *pdf_xobj << "   /Length " << strlen(os.str().c_str()) << "\n";\r
788                     *pdf_xobj << ">>\n";\r
789                     *pdf_xobj << "stream\n";\r
790                     *pdf_xobj << os.str().c_str();\r
791                     *pdf_xobj << "endstream\n";\r
792 \r
793 \r
794                     pdf_file->end_resource(pdf_alpha);\r
795                     pdf_file->end_resource(pdf_smask);\r
796                     pdf_file->end_resource(pdf_xobj);\r
797                 }\r
798                 \r
799                 *pdf_gstate << ">>\n";\r
800                 \r
801                 *page_stream << pdf_gstate->get_name() << " gs\n";\r
802                 \r
803                 pdf_file->end_resource(pdf_gstate);\r
804             }\r
805         }\r
806     }\r
807 }\r
808 \r
809 void\r
810 PrintPDF::print_fill_style(SVGOStringStream &os, SPStyle const *const style, NRRect const *pbox)\r
811 {\r
812     g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR\r
813                       || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
814                            && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) );\r
815     \r
816     if (style->fill.type == SP_PAINT_TYPE_COLOR) {\r
817         float rgb[3];\r
818         sp_color_get_rgb_floatv(&style->fill.value.color, rgb);\r
819 \r
820         *page_stream << rgb[0] << " " << rgb[1] << " " << rgb[2] << " rg\n";\r
821     } else {\r
822         g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
823                   && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
824 \r
825         if (SP_IS_LINEARGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
826 \r
827             SPLinearGradient *lg=SP_LINEARGRADIENT(SP_STYLE_FILL_SERVER (style));\r
828             NR::Point p1 (lg->x1.computed, lg->y1.computed);\r
829             NR::Point p2 (lg->x2.computed, lg->y2.computed);\r
830             if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
831                 // convert to userspace\r
832                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
833                 p1 *= bbox2user;\r
834                 p2 *= bbox2user;\r
835             }\r
836 \r
837             PdfObject *pdf_shade = pdf_file->begin_resource(pdf_shading);\r
838 \r
839             *pdf_shade << "<<\n/ShadingType 2\n/ColorSpace /DeviceRGB\n";\r
840             *pdf_shade << "/Coords [" << p1[NR::X] << " " << p1[NR::Y] << " " << p2[NR::X] << " " << p2[NR::Y] <<"]\n";\r
841             *pdf_shade << "/Extend [true true]\n";\r
842             *pdf_shade << "/Domain [0 1]\n";\r
843             *pdf_shade << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
844 \r
845             sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built\r
846             for (gint i = 0; unsigned(i) < lg->vector.stops.size() - 1; i++) {\r
847                 float rgb[3];\r
848                 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);\r
849                 *pdf_shade << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
850                 *pdf_shade << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
851                 sp_color_get_rgb_floatv(&lg->vector.stops[i+1].color, rgb);\r
852                 *pdf_shade << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
853                 *pdf_shade << "/N 1\n>>\n";\r
854             }\r
855             *pdf_shade << "]\n/Domain [0 1]\n";\r
856             *pdf_shade << "/Bounds [ ";\r
857             for (gint i=0;unsigned(i)<lg->vector.stops.size()-2;i++) {\r
858                 *pdf_shade << lg->vector.stops[i+1].offset <<" ";\r
859             }\r
860             *pdf_shade << "]\n";\r
861             *pdf_shade << "/Encode [ ";\r
862             for (gint i=0;unsigned(i)<lg->vector.stops.size()-1;i++) {\r
863                 *pdf_shade << "0 1 ";\r
864             }\r
865             *pdf_shade << "]\n";\r
866             *pdf_shade << ">>\n>>\n";\r
867             *page_stream << pdf_shade->get_name() << " ";\r
868 \r
869             pdf_file->end_resource(pdf_shade);\r
870         } else if (SP_IS_RADIALGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
871 \r
872             SPRadialGradient *rg=SP_RADIALGRADIENT(SP_STYLE_FILL_SERVER (style));\r
873             NR::Point c (rg->cx.computed, rg->cy.computed);\r
874             NR::Point f (rg->fx.computed, rg->fy.computed);\r
875             double r = rg->r.computed;\r
876             if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
877                 // convert to userspace\r
878                 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
879                 c *= bbox2user;\r
880                 f *= bbox2user;\r
881                 r *= bbox2user.expansion();\r
882             }\r
883 \r
884             PdfObject *pdf_shade = pdf_file->begin_resource(pdf_shading);\r
885 \r
886             *pdf_shade << "<<\n/ShadingType 3\n/ColorSpace /DeviceRGB\n";\r
887             *pdf_shade << "/Coords ["<< f[NR::X] <<" "<< f[NR::Y] <<" 0 "<< c[NR::X] <<" "<< c[NR::Y] <<" "<< r <<"]\n";\r
888             *pdf_shade << "/Extend [true true]\n";\r
889             *pdf_shade << "/Domain [0 1]\n";\r
890             *pdf_shade << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
891 \r
892             sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built\r
893             for (gint i = 0; unsigned(i) < rg->vector.stops.size() - 1; i++) {\r
894                 float rgb[3];\r
895                 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);\r
896                 *pdf_shade << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
897                 *pdf_shade << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
898                 sp_color_get_rgb_floatv(&rg->vector.stops[i+1].color, rgb);\r
899                 *pdf_shade << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
900                 *pdf_shade << "/N 1\n>>\n";\r
901             }\r
902             *pdf_shade << "]\n/Domain [0 1]\n";\r
903             *pdf_shade << "/Bounds [ ";\r
904 \r
905             for (gint i=0;unsigned(i)<rg->vector.stops.size()-2;i++) {\r
906                 *pdf_shade << rg->vector.stops[i+1].offset <<" ";\r
907             }\r
908             *pdf_shade << "]\n";\r
909             *pdf_shade << "/Encode [ ";\r
910             for (gint i=0;unsigned(i)<rg->vector.stops.size()-1;i++) {\r
911                 *pdf_shade << "0 1 ";\r
912             }\r
913             *pdf_shade << "]\n";\r
914             *pdf_shade << ">>\n>>\n";\r
915 \r
916             *page_stream << pdf_shade->get_name() << " ";\r
917 \r
918             pdf_file->end_resource(pdf_shade);\r
919         }\r
920     }\r
921 }\r
922 \r
923 void\r
924 PrintPDF::print_stroke_style(SVGOStringStream &os, SPStyle const *style)\r
925 {\r
926     float rgb[3];\r
927     \r
928     sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);\r
929     *page_stream << rgb[0] << " " << rgb[1] << " " << rgb[2] << " RG\n";\r
930         \r
931     float alpha = 1.0;\r
932     alpha *= SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);\r
933     alpha *= _pushed_alphas[_curr_alpha];\r
934 \r
935     if (alpha != 1.0) {\r
936         PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_extgstate);\r
937         *pdf_alpha << "<< /Type /ExtGState\n";\r
938         *pdf_alpha << "   /CA " << alpha << "\n";\r
939         *pdf_alpha << "   /AIS false\n";\r
940         *pdf_alpha << ">>\n";\r
941         \r
942         *page_stream << pdf_alpha->get_name() << " gs\n";\r
943         \r
944         pdf_file->end_resource(pdf_alpha);\r
945     }\r
946 \r
947     // There are rare cases in which for a solid line stroke_dasharray_set is true. To avoid\r
948     // invalid PS-lines such as "[0.0000000 0.0000000] 0.0000000 setdash", which should be "[] 0 setdash",\r
949     // we first check if all components of stroke_dash.dash are 0.\r
950     bool LineSolid = true;\r
951     if (style->stroke_dash.n_dash   &&\r
952         style->stroke_dash.dash       )\r
953     {\r
954         int i = 0;\r
955         while (LineSolid && (i < style->stroke_dash.n_dash)) {\r
956                 if (style->stroke_dash.dash[i] > 0.00000001)\r
957                     LineSolid = false;\r
958                 i++;\r
959         }\r
960         if (!LineSolid) {\r
961             *page_stream << "[";\r
962             for (i = 0; i < style->stroke_dash.n_dash; i++) {\r
963                 if (i > 0) {\r
964                     *page_stream << " ";\r
965                 }\r
966                 *page_stream << style->stroke_dash.dash[i];\r
967             }\r
968             *page_stream << "] " << style->stroke_dash.offset << " d\n";\r
969         } else {\r
970             *page_stream << "[] 0 d\n";\r
971         }\r
972     } else {\r
973         *page_stream << "[] 0 d\n";\r
974     }\r
975 \r
976     *page_stream << style->stroke_width.computed << " w\n";\r
977     *page_stream << style->stroke_linejoin.computed << " j\n";\r
978     *page_stream << style->stroke_linecap.computed << " J\n";\r
979     *page_stream <<\r
980         ( style->stroke_miterlimit.value > 1 ?\r
981           style->stroke_miterlimit.value : 1 ) << " M\n";\r
982 }\r
983 \r
984 \r
985 unsigned int\r
986 PrintPDF::fill(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *const style,\r
987               NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)\r
988 {\r
989     Inkscape::SVGOStringStream os;\r
990     os.setf(std::ios::fixed);\r
991 \r
992     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
993     if (_bitmap) return 0;\r
994 \r
995     if ( style->fill.type == SP_PAINT_TYPE_COLOR ) {\r
996         *page_stream << "q\n";\r
997         print_fill_style(os, style, pbox);\r
998         print_fill_alpha(os, style, pbox);\r
999         print_bpath(os, bpath->path);\r
1000         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {\r
1001             *page_stream << "f*\n";\r
1002         } else {\r
1003             *page_stream << "f\n";\r
1004         }\r
1005         *page_stream << "Q\n";\r
1006     }\r
1007     else if ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1008               && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) )\r
1009     {\r
1010         *page_stream << "q\n";\r
1011         print_bpath(os, bpath->path);\r
1012 \r
1013         if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {\r
1014             g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1015                       && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
1016             SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
1017             *page_stream << "W* n\n";\r
1018             print_fill_alpha(os, style, pbox);\r
1019             if (g->gradientTransform_set) {\r
1020                 *page_stream << "q\n";\r
1021                 *page_stream << g->gradientTransform[0] << " "\r
1022                              << g->gradientTransform[1] << " "\r
1023                              << g->gradientTransform[2] << " "\r
1024                              << g->gradientTransform[3] << " "\r
1025                              << g->gradientTransform[4] << " "\r
1026                              << g->gradientTransform[5] << " cm\n";\r
1027             }\r
1028             print_fill_style(os, style, pbox);\r
1029             *page_stream << "sh\n";\r
1030             if (g->gradientTransform_set) {\r
1031                 *page_stream << "Q\n";\r
1032             }\r
1033         } else {\r
1034             g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1035                       && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
1036             SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
1037             *page_stream << "W n\n";\r
1038             print_fill_alpha(os, style, pbox);\r
1039             if (g->gradientTransform_set) {\r
1040                 *page_stream << "q\n";\r
1041                 *page_stream << g->gradientTransform[0] << " "\r
1042                              << g->gradientTransform[1] << " "\r
1043                              << g->gradientTransform[2] << " "\r
1044                              << g->gradientTransform[3] << " "\r
1045                              << g->gradientTransform[4] << " "\r
1046                              << g->gradientTransform[5] << " cm\n"; \r
1047             }\r
1048             print_fill_style(os, style, pbox);\r
1049             *page_stream << "sh\n";\r
1050             if (g->gradientTransform_set) {\r
1051                 *page_stream << "Q\n";\r
1052             }\r
1053         }\r
1054 \r
1055         *page_stream << "Q\n";\r
1056     }        \r
1057 \r
1058     return 0;\r
1059 }\r
1060 \r
1061 \r
1062 unsigned int\r
1063 PrintPDF::stroke(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style,\r
1064                 NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)\r
1065 {\r
1066     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
1067     if (_bitmap) return 0;\r
1068 \r
1069     if (style->stroke.type == SP_PAINT_TYPE_COLOR) {\r
1070         Inkscape::SVGOStringStream os;\r
1071         os.setf(std::ios::fixed);\r
1072 \r
1073         *page_stream << "q\n";\r
1074 \r
1075         print_stroke_style(os, style);\r
1076         print_bpath(os, bpath->path);\r
1077         *page_stream << "S\n";\r
1078 \r
1079         *page_stream << "Q\n";\r
1080     }\r
1081 \r
1082     return 0;\r
1083 }\r
1084 \r
1085 unsigned int\r
1086 PrintPDF::image(Inkscape::Extension::Print *mod, guchar *px, unsigned int w, unsigned int h, unsigned int rs,\r
1087                NRMatrix const *transform, SPStyle const *style)\r
1088 {\r
1089     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
1090     if (_bitmap) return 0;\r
1091 \r
1092     return 0;\r
1093     \r
1094     return print_image(_stream, px, w, h, rs, transform);\r
1095 #if 0\r
1096     fprintf(_stream, "gsave\n");\r
1097     fprintf(_stream, "/rowdata %d string def\n", 3 * w);\r
1098     fprintf(_stream, "[%g %g %g %g %g %g] concat\n",\r
1099             transform->c[0],\r
1100             transform->c[1],\r
1101             transform->c[2],\r
1102             transform->c[3],\r
1103             transform->c[4],\r
1104             transform->c[5]);\r
1105     fprintf(_stream, "%d %d 8 [%d 0 0 -%d 0 %d]\n", w, h, w, h, h);\r
1106     fprintf(_stream, "{currentfile rowdata readhexstring pop}\n");\r
1107     fprintf(_stream, "false 3 colorimage\n");\r
1108 \r
1109     for (unsigned int r = 0; r < h; r++) {\r
1110         guchar *s;\r
1111         unsigned int c0, c1, c;\r
1112         s = px + r * rs;\r
1113         for (c0 = 0; c0 < w; c0 += 24) {\r
1114             c1 = MIN(w, c0 + 24);\r
1115             for (c = c0; c < c1; c++) {\r
1116                 static char const xtab[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};\r
1117                 fputc(xtab[s[0] >> 4], _stream);\r
1118                 fputc(xtab[s[0] & 0xf], _stream);\r
1119                 fputc(xtab[s[1] >> 4], _stream);\r
1120                 fputc(xtab[s[1] & 0xf], _stream);\r
1121                 fputc(xtab[s[2] >> 4], _stream);\r
1122                 fputc(xtab[s[2] & 0xf], _stream);\r
1123                 s += 4;\r
1124             }\r
1125             fputs("\n", _stream);\r
1126         }\r
1127     }\r
1128 \r
1129     fprintf(_stream, "grestore\n");\r
1130 \r
1131     return 0;\r
1132 #endif\r
1133 }\r
1134 \r
1135 char const *\r
1136 PrintPDF::PSFontName(SPStyle const *style)\r
1137 {\r
1138     font_instance *tf = (font_factory::Default())->Face(style->text->font_family.value, font_style_to_pos(*style));\r
1139 \r
1140     char const *n;\r
1141     char name_buf[256];\r
1142 \r
1143     if (tf) {\r
1144         tf->PSName(name_buf, sizeof(name_buf));\r
1145         n = name_buf;\r
1146         tf->Unref();\r
1147     } else {\r
1148         // 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\r
1149         bool i = (style->font_style.value == SP_CSS_FONT_STYLE_ITALIC);\r
1150         bool o = (style->font_style.value == SP_CSS_FONT_STYLE_OBLIQUE);\r
1151         bool b = (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) ||\r
1152             (style->font_weight.value >= SP_CSS_FONT_WEIGHT_500 && style->font_weight.value <= SP_CSS_FONT_WEIGHT_900);\r
1153 \r
1154         n = g_strdup_printf("%s%s%s%s",\r
1155                             g_strdelimit(style->text->font_family.value, " ", '-'),\r
1156                             (b || i || o) ? "-" : "",\r
1157                             (b) ? "Bold" : "",\r
1158                             (i) ? "Italic" : ((o) ? "Oblique" : "") );\r
1159     }\r
1160 \r
1161     return g_strdup(n);\r
1162 }\r
1163 \r
1164 \r
1165 unsigned int\r
1166 PrintPDF::text(Inkscape::Extension::Print *mod, char const *text, NR::Point p,\r
1167               SPStyle const *const style)\r
1168 {\r
1169     if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
1170     if (_bitmap) return 0;\r
1171 \r
1172     Inkscape::SVGOStringStream os;\r
1173     os.setf(std::ios::fixed);\r
1174 \r
1175     return 0;\r
1176     \r
1177     // Escape chars\r
1178     Inkscape::SVGOStringStream escaped_text;\r
1179     escaped_text.setf(std::ios::fixed);\r
1180     \r
1181     escaped_text << std::oct;\r
1182     for (gchar const *p_text = text ; *p_text ; p_text = g_utf8_next_char(p_text)) {\r
1183         gunichar const c = g_utf8_get_char(p_text);\r
1184         if (c == '\\' || c == ')' || c == '(')\r
1185             escaped_text << '\\' << static_cast<char>(c);\r
1186         else if (c >= 0x80)\r
1187             escaped_text << '\\' << c;\r
1188         else\r
1189             escaped_text << static_cast<char>(c);\r
1190     }\r
1191 \r
1192     os << "gsave\n";\r
1193 \r
1194     // set font\r
1195     char const *fn = PSFontName(style);\r
1196     if (_latin1_encoded_fonts.find(fn) == _latin1_encoded_fonts.end()) {\r
1197         if (!_newlatin1font_proc_defined) {\r
1198             // input: newfontname, existingfontname\r
1199             // output: new font object, also defined to newfontname\r
1200             os << "/newlatin1font "         // name of the proc\r
1201                   "{findfont dup length dict copy "     // load the font and create a copy of it\r
1202                   "dup /Encoding ISOLatin1Encoding put "     // change the encoding in the copy\r
1203                   "definefont} def\n";      // create the new font and leave it on the stack, define the proc\r
1204             _newlatin1font_proc_defined = true;\r
1205         }\r
1206         os << "/" << fn << "-ISOLatin1 /" << fn << " newlatin1font\n";\r
1207         _latin1_encoded_fonts.insert(fn);\r
1208     } else\r
1209         os << "/" << fn << "-ISOLatin1 findfont\n";\r
1210     os << style->font_size.computed << " scalefont\n";\r
1211     os << "setfont\n";\r
1212     g_free((void *) fn);\r
1213 \r
1214     if ( style->fill.type == SP_PAINT_TYPE_COLOR\r
1215          || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1216               && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) )\r
1217     {\r
1218         // set fill style\r
1219         print_fill_style(os, style, NULL);\r
1220         // FIXME: we don't know the pbox of text, so have to pass NULL. This means gradients with\r
1221         // bbox units won't work with text. However userspace gradients don't work with text either\r
1222         // (text is black) for some reason.\r
1223 \r
1224         os << "newpath\n";\r
1225         os << p[NR::X] << " " << p[NR::Y] << " moveto\n";\r
1226         os << "(" << escaped_text.str() << ") show\n";\r
1227     }\r
1228 \r
1229     if (style->stroke.type == SP_PAINT_TYPE_COLOR) {\r
1230 \r
1231         // set stroke style\r
1232         print_stroke_style(os, style);\r
1233 \r
1234         // paint stroke\r
1235         os << "newpath\n";\r
1236         os << p[NR::X] << " " << p[NR::Y] << " moveto\n";\r
1237         os << "(" << escaped_text.str() << ") false charpath stroke\n";\r
1238     }\r
1239 \r
1240     os << "grestore\n";\r
1241 \r
1242     fprintf(_stream, "%s", os.str().c_str());\r
1243 \r
1244     return 0;\r
1245 }\r
1246 \r
1247 \r
1248 \r
1249 /* PDF helpers */\r
1250 \r
1251 void\r
1252 PrintPDF::print_bpath(SVGOStringStream &os, NArtBpath const *bp)\r
1253 {\r
1254     bool closed = false;\r
1255     while (bp->code != NR_END) {\r
1256         switch (bp->code) {\r
1257             case NR_MOVETO:\r
1258                 if (closed) {\r
1259                     *page_stream << "h\n";\r
1260                 }\r
1261                 closed = true;\r
1262                 *page_stream << bp->x3 << " " << bp->y3 << " m\n";\r
1263                 break;\r
1264             case NR_MOVETO_OPEN:\r
1265                 if (closed) {\r
1266                     *page_stream << "h\n";\r
1267                 }\r
1268                 closed = false;\r
1269                 *page_stream << bp->x3 << " " << bp->y3 << " m\n";\r
1270                 break;\r
1271             case NR_LINETO:\r
1272                 *page_stream << bp->x3 << " " << bp->y3 << " l\n";\r
1273                 break;\r
1274             case NR_CURVETO:\r
1275                 *page_stream << bp->x1 << " " << bp->y1 << " "\r
1276                    << bp->x2 << " " << bp->y2 << " "\r
1277                    << bp->x3 << " " << bp->y3 << " c\n";\r
1278                 break;\r
1279             default:\r
1280                 break;\r
1281         }\r
1282         bp += 1;\r
1283     }\r
1284     if (closed) {\r
1285         *page_stream << "h\n";\r
1286     }\r
1287 }\r
1288 \r
1289 /* The following code is licensed under GNU GPL.\r
1290 ** The packbits, ascii85 and imaging printing code\r
1291 ** is from the gimp's postscript.c.\r
1292 */\r
1293 \r
1294 /**\r
1295 * \param nin Number of bytes of source data.\r
1296 * \param src Source data.\r
1297 * \param nout Number of output bytes.\r
1298 * \param dst Buffer for output.\r
1299 */\r
1300 void\r
1301 PrintPDF::compress_packbits(int nin,\r
1302                            guchar *src,\r
1303                            int *nout,\r
1304                            guchar *dst)\r
1305 \r
1306 {\r
1307     register guchar c;\r
1308     int nrepeat, nliteral;\r
1309     guchar *run_start;\r
1310     guchar *start_dst = dst;\r
1311     guchar *last_literal = NULL;\r
1312 \r
1313     for (;;) {\r
1314         if (nin <= 0) break;\r
1315 \r
1316         run_start = src;\r
1317         c = *run_start;\r
1318 \r
1319         /* Search repeat bytes */\r
1320         if ((nin > 1) && (c == src[1])) {\r
1321             nrepeat = 1;\r
1322             nin -= 2;\r
1323             src += 2;\r
1324             while ((nin > 0) && (c == *src)) {\r
1325                 nrepeat++;\r
1326                 src++;\r
1327                 nin--;\r
1328                 if (nrepeat == 127) break; /* Maximum repeat */\r
1329             }\r
1330 \r
1331             /* Add two-byte repeat to last literal run ? */\r
1332             if ( (nrepeat == 1)\r
1333                  && (last_literal != NULL) && (((*last_literal)+1)+2 <= 128) )\r
1334             {\r
1335                 *last_literal += 2;\r
1336                 *(dst++) = c;\r
1337                 *(dst++) = c;\r
1338                 continue;\r
1339             }\r
1340 \r
1341             /* Add repeat run */\r
1342             *(dst++) = (guchar)((-nrepeat) & 0xff);\r
1343             *(dst++) = c;\r
1344             last_literal = NULL;\r
1345             continue;\r
1346         }\r
1347         /* Search literal bytes */\r
1348         nliteral = 1;\r
1349         nin--;\r
1350         src++;\r
1351 \r
1352         for (;;) {\r
1353             if (nin <= 0) break;\r
1354 \r
1355             if ((nin >= 2) && (src[0] == src[1])) /* A two byte repeat ? */\r
1356                 break;\r
1357 \r
1358             nliteral++;\r
1359             nin--;\r
1360             src++;\r
1361             if (nliteral == 128) break; /* Maximum literal run */\r
1362         }\r
1363 \r
1364         /* Could be added to last literal run ? */\r
1365         if ((last_literal != NULL) && (((*last_literal)+1)+nliteral <= 128)) {\r
1366             *last_literal += nliteral;\r
1367         } else {\r
1368             last_literal = dst;\r
1369             *(dst++) = (guchar)(nliteral-1);\r
1370         }\r
1371         while (nliteral-- > 0) *(dst++) = *(run_start++);\r
1372     }\r
1373     *nout = dst - start_dst;\r
1374 }\r
1375 \r
1376 void\r
1377 PrintPDF::ascii85_init(void)\r
1378 {\r
1379     ascii85_len = 0;\r
1380     ascii85_linewidth = 0;\r
1381 }\r
1382 \r
1383 void\r
1384 PrintPDF::ascii85_flush(SVGOStringStream &os)\r
1385 {\r
1386     char c[5];\r
1387     bool const zero_case = (ascii85_buf == 0);\r
1388     static int const max_linewidth = 75;\r
1389 \r
1390     for (int i = 4; i >= 0; i--) {\r
1391         c[i] = (ascii85_buf % 85) + '!';\r
1392         ascii85_buf /= 85;\r
1393     }\r
1394     /* check for special case: "!!!!!" becomes "z", but only if not\r
1395      * at end of data. */\r
1396     if (zero_case && (ascii85_len == 4)) {\r
1397         if (ascii85_linewidth >= max_linewidth) {\r
1398             os << '\n';\r
1399             ascii85_linewidth = 0;\r
1400         }\r
1401         os << 'z';\r
1402         ascii85_linewidth++;\r
1403     } else {\r
1404         for (int i = 0; i < ascii85_len+1; i++) {\r
1405             if ((ascii85_linewidth >= max_linewidth) && (c[i] != '%')) {\r
1406                 os << '\n';\r
1407                 ascii85_linewidth = 0;\r
1408             }\r
1409             os << c[i];\r
1410             ascii85_linewidth++;\r
1411         }\r
1412     }\r
1413 \r
1414     ascii85_len = 0;\r
1415     ascii85_buf = 0;\r
1416 }\r
1417 \r
1418 inline void\r
1419 PrintPDF::ascii85_out(guchar byte, SVGOStringStream &os)\r
1420 {\r
1421     if (ascii85_len == 4)\r
1422         ascii85_flush(os);\r
1423 \r
1424     ascii85_buf <<= 8;\r
1425     ascii85_buf |= byte;\r
1426     ascii85_len++;\r
1427 }\r
1428 \r
1429 void\r
1430 PrintPDF::ascii85_nout(int n, guchar *uptr, SVGOStringStream &os)\r
1431 {\r
1432     while (n-- > 0) {\r
1433         ascii85_out(*uptr, os);\r
1434         uptr++;\r
1435     }\r
1436 }\r
1437 \r
1438 void\r
1439 PrintPDF::ascii85_done(SVGOStringStream &os)\r
1440 {\r
1441     if (ascii85_len) {\r
1442         /* zero any unfilled buffer portion, then flush */\r
1443         ascii85_buf <<= (8 * (4-ascii85_len));\r
1444         ascii85_flush(os);\r
1445     }\r
1446 \r
1447     os << "~>\n";\r
1448 }\r
1449 \r
1450 unsigned int\r
1451 PrintPDF::print_image(FILE *ofp, guchar *px, unsigned int width, unsigned int height, unsigned int rs,\r
1452                      NRMatrix const *transform)\r
1453 {\r
1454     Inkscape::SVGOStringStream os;\r
1455     os.setf(std::ios::fixed);\r
1456 \r
1457     return 0;\r
1458     \r
1459     os << "gsave\n";\r
1460     os << "[" << transform->c[0] << " "\r
1461        << transform->c[1] << " "\r
1462        << transform->c[2] << " "\r
1463        << transform->c[3] << " "\r
1464        << transform->c[4] << " "\r
1465        << transform->c[5] << "] concat\n";\r
1466     os << width << " " << height << " 8 ["\r
1467        << width << " 0 0 -" << height << " 0 " << height << "]\n";\r
1468 \r
1469 \r
1470     /* Write read image procedure */\r
1471     os << "% Strings to hold RGB-samples per scanline\n";\r
1472     os << "/rstr " << width << " string def\n";\r
1473     os << "/gstr " << width << " string def\n";\r
1474     os << "/bstr " << width << " string def\n";\r
1475     os << "{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop}\n";\r
1476     os << "{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop}\n";\r
1477     os << "{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop}\n";\r
1478     os << "true 3\n";\r
1479 \r
1480     /* Allocate buffer for packbits data. Worst case: Less than 1% increase */\r
1481     guchar *const packb = (guchar *)g_malloc((width * 105)/100+2);\r
1482     guchar *const plane = (guchar *)g_malloc(width);\r
1483 \r
1484     /* ps_begin_data(ofp); */\r
1485     os << "colorimage\n";\r
1486 \r
1487 /*#define GET_RGB_TILE(begin)                   \\r
1488  *  {int scan_lines;                                                    \\r
1489  *    scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \\r
1490  *    gimp_pixel_rgn_get_rect(&pixel_rgn, begin, 0, i, width, scan_lines); \\r
1491  *    src = begin; }\r
1492  */\r
1493 \r
1494     for (unsigned i = 0; i < height; i++) {\r
1495         /* if ((i % tile_height) == 0) GET_RGB_TILE(data); */ /* Get more data */\r
1496         guchar const *const src = px + i * rs;\r
1497 \r
1498         /* Iterate over RGB */\r
1499         for (int rgb = 0; rgb < 3; rgb++) {\r
1500             guchar const *src_ptr = src + rgb;\r
1501             guchar *plane_ptr = plane;\r
1502             for (unsigned j = 0; j < width; j++) {\r
1503                 *(plane_ptr++) = *src_ptr;\r
1504                 src_ptr += 4;\r
1505             }\r
1506 \r
1507             int nout;\r
1508             compress_packbits(width, plane, &nout, packb);\r
1509 \r
1510             ascii85_init();\r
1511             ascii85_nout(nout, packb, os);\r
1512             ascii85_out(128, os); /* Write EOD of RunLengthDecode filter */\r
1513             ascii85_done(os);\r
1514         }\r
1515     }\r
1516     /* ps_end_data(ofp); */\r
1517 \r
1518 #if 0\r
1519     fprintf(ofp, "showpage\n");\r
1520     g_free(data);\r
1521 #endif\r
1522 \r
1523     g_free(packb);\r
1524     g_free(plane);\r
1525 \r
1526 #if 0\r
1527     if (ferror(ofp)) {\r
1528         g_message(_("write error occurred"));\r
1529         return (FALSE);\r
1530     }\r
1531 #endif\r
1532 \r
1533     os << "grestore\n";\r
1534 \r
1535     fprintf(ofp, "%s", os.str().c_str());\r
1536 \r
1537     return 0;\r
1538 //#undef GET_RGB_TILE\r
1539 }\r
1540 \r
1541 bool\r
1542 PrintPDF::textToPath(Inkscape::Extension::Print * ext)\r
1543 {\r
1544     return ext->get_param_bool("textToPath");\r
1545 }\r
1546 \r
1547 #include "clear-n_.h"\r
1548 \r
1549 void\r
1550 PrintPDF::init(void)\r
1551 {\r
1552     /* SVG in */\r
1553     (void) Inkscape::Extension::build_from_mem(\r
1554         "<inkscape-extension>\n"\r
1555         "<name>" N_("PDF Print") "</name>\n"\r
1556         "<id>" SP_MODULE_KEY_PRINT_PDF "</id>\n"\r
1557         "<param name=\"bitmap\" type=\"boolean\">FALSE</param>\n"\r
1558         "<param name=\"resolution\" type=\"string\">72</param>\n"\r
1559         "<param name=\"destination\" type=\"string\">| lp</param>\n"\r
1560         "<param name=\"pageBoundingBox\" type=\"boolean\">TRUE</param>\n"\r
1561         "<param name=\"textToPath\" type=\"boolean\">TRUE</param>\n"\r
1562         "<print/>\n"\r
1563         "</inkscape-extension>", new PrintPDF());\r
1564 }\r
1565 \r
1566 \r
1567 }  /* namespace Internal */\r
1568 }  /* namespace Extension */\r
1569 }  /* namespace Inkscape */\r
1570 \r
1571 /* End of GNU GPL code */\r
1572 \r
1573 \r
1574 /*\r
1575   Local Variables:\r
1576   mode:c++\r
1577   c-file-style:"stroustrup"\r
1578   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
1579   indent-tabs-mode:nil\r
1580   fill-column:99\r
1581   End:\r
1582 */\r
1583 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r