Code

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