Code

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