Code

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