b732fc7231e73a7d6a335fb4b6d23a99c62e6ce2
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 bool epsexport = false;\r
229 \r
230 _latin1_encoded_fonts.clear();\r
231 _newlatin1font_proc_defined = false;\r
232 \r
233 FILE *osf = NULL;\r
234 FILE *osp = NULL;\r
235 \r
236 _pushed_alphas.clear();\r
237 _pushed_alphas.push_back(1.0);\r
238 \r
239 gsize bytesRead = 0;\r
240 gsize bytesWritten = 0;\r
241 GError *error = NULL;\r
242 gchar const *utf8_fn = mod->get_param_string("destination");\r
243 gchar *local_fn = g_filename_from_utf8( utf8_fn,\r
244 -1, &bytesRead, &bytesWritten, &error);\r
245 gchar const *fn = local_fn;\r
246 \r
247 /* TODO: Replace the below fprintf's with something that does the right thing whether in\r
248 * gui or batch mode (e.g. --print=blah). Consider throwing an exception: currently one of\r
249 * the callers (sp_print_document_to_file, "ret = mod->begin(doc)") wrongly ignores the\r
250 * return code.\r
251 */\r
252 if (fn != NULL) {\r
253 if (*fn == '|') {\r
254 fn += 1;\r
255 while (isspace(*fn)) fn += 1;\r
256 #ifndef WIN32\r
257 osp = popen(fn, "wb");\r
258 #else\r
259 osp = _popen(fn, "wb");\r
260 #endif\r
261 if (!osp) {\r
262 fprintf(stderr, "inkscape: popen(%s): %s\n",\r
263 fn, strerror(errno));\r
264 return 0;\r
265 }\r
266 _stream = osp;\r
267 } else if (*fn == '>') {\r
268 fn += 1;\r
269 epsexport = g_str_has_suffix(fn,".eps");\r
270 while (isspace(*fn)) fn += 1;\r
271 Inkscape::IO::dump_fopen_call(fn, "K");\r
272 osf = Inkscape::IO::fopen_utf8name(fn, "wb+");\r
273 if (!osf) {\r
274 fprintf(stderr, "inkscape: fopen(%s): %s\n",\r
275 fn, strerror(errno));\r
276 return 0;\r
277 }\r
278 _stream = osf;\r
279 } else {\r
280 /* put cwd stuff in here */\r
281 gchar *qn = ( *fn\r
282 ? g_strdup_printf("lpr -P %s", fn) /* FIXME: quote fn */\r
283 : g_strdup("lpr") );\r
284 #ifndef WIN32\r
285 osp = popen(qn, "wb");\r
286 #else\r
287 osp = _popen(qn, "wb");\r
288 #endif\r
289 if (!osp) {\r
290 fprintf(stderr, "inkscape: popen(%s): %s\n",\r
291 qn, strerror(errno));\r
292 return 0;\r
293 }\r
294 g_free(qn);\r
295 _stream = osp;\r
296 }\r
297 }\r
298 \r
299 g_free(local_fn);\r
300 \r
301 if (_stream) {\r
302 /* fixme: this is kinda icky */\r
303 #if !defined(_WIN32) && !defined(__WIN32__)\r
304 (void) signal(SIGPIPE, SIG_IGN);\r
305 #endif\r
306 }\r
307 \r
308 pdf_file = new PdfFile(_stream);\r
309 doc_info = pdf_file->begin_document(1.4);\r
310 *doc_info << " /Title(" << doc->name << ")\n";\r
311 *doc_info << " /Author(" << g_get_real_name() << ")\n";\r
312 // *doc_info << " /Subject(" << "?" << ")\n";\r
313 // *doc_info << " /Keywords(" << "?" << ")\n";\r
314 *doc_info << " /Creator(" << "www.inkscape.org" << ")\n";\r
315 *doc_info << " /Producer(" << "Inkscape " << PACKAGE_STRING << ")\n";\r
316 //the date should be in ISO/IEC 8824\r
317 GDate date;\r
318 GTimeVal ltime;\r
319 glong time_hh, time_mm, time_ss;\r
320 #if GLIB_CHECK_VERSION(2,9,0)\r
321 g_date_set_time_t (&date, time (NULL));\r
322 #else\r
323 g_date_set_time(&date, time (NULL));\r
324 #endif\r
325 gchar date_str[100], time_str[100];\r
326 g_date_strftime(date_str, 99, "%Y%m%d", &date);\r
327 g_get_current_time(<ime);\r
328 time_hh=(ltime.tv_sec/3600)%24;\r
329 time_mm=(ltime.tv_sec/60)%60;\r
330 time_ss=(ltime.tv_sec)%60;\r
331 g_snprintf(time_str, 99, "%02ld%02ld%02ld", time_hh, time_mm, time_ss); \r
332 *doc_info << " /CreationDate(D:" << date_str << time_str << "Z)\n";\r
333 // *doc_info << " /CreationDate(D:" << date_str << time_str << "OHH'mm')\n";\r
334 \r
335 /* flush this to test output stream as early as possible */\r
336 if (fflush(_stream)) {\r
337 /*g_print("caught error in sp_module_print_plain_begin\n");*/\r
338 if (ferror(_stream)) {\r
339 g_print("Error %d on output stream: %s\n", errno,\r
340 g_strerror(errno));\r
341 }\r
342 g_print("Printing failed\n");\r
343 /* fixme: should use pclose() for pipes */\r
344 fclose(_stream);\r
345 _stream = NULL;\r
346 fflush(stdout);\r
347 return 0;\r
348 }\r
349 \r
350 // width and height in pt\r
351 _width = sp_document_width(doc) * PT_PER_PX;\r
352 _height = sp_document_height(doc) * PT_PER_PX;\r
353 \r
354 NRRect d;\r
355 bool pageBoundingBox;\r
356 pageBoundingBox = mod->get_param_bool("pageBoundingBox");\r
357 // printf("Page Bounding Box: %s\n", pageBoundingBox ? "TRUE" : "FALSE");\r
358 if (pageBoundingBox) {\r
359 d.x0 = d.y0 = 0;\r
360 d.x1 = _width;\r
361 d.y1 = _height;\r
362 } else {\r
363 SPItem* doc_item = SP_ITEM(sp_document_root(doc));\r
364 sp_item_invoke_bbox(doc_item, &d, sp_item_i2r_affine(doc_item), TRUE);\r
365 // convert from px to pt\r
366 d.x0 *= PT_PER_PX;\r
367 d.x1 *= PT_PER_PX;\r
368 d.y0 *= PT_PER_PX;\r
369 d.y1 *= PT_PER_PX;\r
370 }\r
371 \r
372 page_stream = pdf_file->begin_page( d.x0, d.y0, d.x1, d.y1 );\r
373 \r
374 if (!_bitmap) {\r
375 Inkscape::SVGOStringStream os;\r
376 os.setf(std::ios::fixed);\r
377 os << PT_PER_PX << " 0 0 "\r
378 << -PT_PER_PX << " 0 " << (int) ceil(_height)\r
379 << " cm\n";\r
380 // from now on we can output px, but they will be treated as pt\r
381 pdf_file->puts(os);\r
382 }\r
383 \r
384 return 1;\r
385 }\r
386 \r
387 unsigned int\r
388 PrintPDF::finish(Inkscape::Extension::Print *mod)\r
389 {\r
390 if (!_stream) return 0;\r
391 \r
392 if (_bitmap) {\r
393 double const dots_per_pt = _dpi / PT_PER_IN;\r
394 \r
395 double const x0 = 0.0;\r
396 double const y0 = 0.0;\r
397 double const x1 = x0 + _width;\r
398 double const y1 = y0 + _height;\r
399 \r
400 /* Bitmap width/height in bitmap dots. */\r
401 int const width = (int) (_width * dots_per_pt + 0.5);\r
402 int const height = (int) (_height * dots_per_pt + 0.5);\r
403 \r
404 NRMatrix affine;\r
405 affine.c[0] = width / ((x1 - x0) * PX_PER_PT);\r
406 affine.c[1] = 0.0;\r
407 affine.c[2] = 0.0;\r
408 affine.c[3] = height / ((y1 - y0) * PX_PER_PT);\r
409 affine.c[4] = -affine.c[0] * x0;\r
410 affine.c[5] = -affine.c[3] * y0;\r
411 \r
412 nr_arena_item_set_transform(mod->root, &affine);\r
413 \r
414 guchar *const px = g_new(guchar, 4 * width * 64);\r
415 \r
416 for (int y = 0; y < height; y += 64) {\r
417 /* Set area of interest. */\r
418 NRRectL bbox;\r
419 bbox.x0 = 0;\r
420 bbox.y0 = y;\r
421 bbox.x1 = width;\r
422 bbox.y1 = MIN(height, y + 64);\r
423 \r
424 /* Update to renderable state. */\r
425 NRGC gc(NULL);\r
426 nr_matrix_set_identity(&gc.transform);\r
427 nr_arena_item_invoke_update(mod->root, &bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);\r
428 /* Render */\r
429 /* This should take guchar* instead of unsigned char*) */\r
430 NRPixBlock pb;\r
431 nr_pixblock_setup_extern(&pb, NR_PIXBLOCK_MODE_R8G8B8A8N,\r
432 bbox.x0, bbox.y0, bbox.x1, bbox.y1,\r
433 (guchar*)px, 4 * width, FALSE, FALSE);\r
434 memset(px, 0xff, 4 * width * 64);\r
435 nr_arena_item_invoke_render(mod->root, &bbox, &pb, 0);\r
436 /* Blitter goes here */\r
437 NRMatrix imgt;\r
438 imgt.c[0] = (bbox.x1 - bbox.x0) / dots_per_pt;\r
439 imgt.c[1] = 0.0;\r
440 imgt.c[2] = 0.0;\r
441 imgt.c[3] = (bbox.y1 - bbox.y0) / dots_per_pt;\r
442 imgt.c[4] = 0.0;\r
443 imgt.c[5] = _height - y / dots_per_pt - (bbox.y1 - bbox.y0) / dots_per_pt;\r
444 \r
445 print_image(_stream, px, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0, 4 * width, &imgt);\r
446 }\r
447 \r
448 g_free(px);\r
449 }\r
450 \r
451 pdf_file->end_page(page_stream);\r
452 pdf_file->end_document(doc_info);\r
453 \r
454 delete pdf_file;\r
455 \r
456 /* Flush stream to be sure. */\r
457 (void) fflush(_stream);\r
458 \r
459 /* fixme: should really use pclose for popen'd streams */\r
460 fclose(_stream);\r
461 _stream = 0;\r
462 _latin1_encoded_fonts.clear();\r
463 \r
464 return 1;\r
465 }\r
466 \r
467 unsigned int\r
468 PrintPDF::bind(Inkscape::Extension::Print *mod, NRMatrix const *transform, float opacity)\r
469 {\r
470 if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
471 if (_bitmap) return 0;\r
472 \r
473 Inkscape::SVGOStringStream os;\r
474 os.setf(std::ios::fixed);\r
475 \r
476 os << "q\n";\r
477 os << transform->c[0] << " "\r
478 << transform->c[1] << " "\r
479 << transform->c[2] << " "\r
480 << transform->c[3] << " "\r
481 << transform->c[4] << " "\r
482 << transform->c[5] << " cm\n";\r
483 \r
484 float alpha = opacity * _pushed_alphas.back();\r
485 _pushed_alphas.push_back(alpha);\r
486 \r
487 pdf_file->puts(os);\r
488 return 1;\r
489 }\r
490 \r
491 unsigned int\r
492 PrintPDF::release(Inkscape::Extension::Print *mod)\r
493 {\r
494 if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
495 if (_bitmap) return 0;\r
496 \r
497 _pushed_alphas.pop_back();\r
498 g_assert( _pushed_alphas.size() > 0 );\r
499 \r
500 pdf_file->puts("Q\n");\r
501 \r
502 return 1;\r
503 }\r
504 \r
505 unsigned int\r
506 PrintPDF::comment(Inkscape::Extension::Print *mod, char const *comment)\r
507 {\r
508 if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
509 if (_bitmap) return 0;\r
510 \r
511 return 1;\r
512 }\r
513 \r
514 void\r
515 PrintPDF::print_fill_alpha(SVGOStringStream &os, SPStyle const *const style, NRRect const *pbox)\r
516 {\r
517 g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR\r
518 || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
519 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) );\r
520 \r
521 if (style->fill.type == SP_PAINT_TYPE_COLOR) {\r
522 float alpha = 1.0;\r
523 alpha *= SP_SCALE24_TO_FLOAT(style->fill_opacity.value);\r
524 alpha *= _pushed_alphas.back();\r
525 \r
526 if (alpha != 1.0) {\r
527 PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_extgstate);\r
528 *pdf_alpha << "<< /Type /ExtGState\n";\r
529 *pdf_alpha << " /ca " << alpha << "\n";\r
530 *pdf_alpha << " /AIS false\n";\r
531 *pdf_alpha << ">>\n";\r
532 \r
533 os << pdf_alpha->get_name()\r
534 << " gs\n";\r
535 \r
536 pdf_file->end_resource(pdf_alpha);\r
537 }\r
538 } else {\r
539 g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
540 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
541 \r
542 if (SP_IS_LINEARGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
543 \r
544 SPLinearGradient *lg=SP_LINEARGRADIENT(SP_STYLE_FILL_SERVER (style));\r
545 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built\r
546 \r
547 NR::Point p1 (lg->x1.computed, lg->y1.computed);\r
548 NR::Point p2 (lg->x2.computed, lg->y2.computed);\r
549 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
550 // convert to userspace\r
551 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
552 p1 *= bbox2user;\r
553 p2 *= bbox2user;\r
554 }\r
555 \r
556 double alpha = 1.0;\r
557 for (gint i = 0; unsigned(i) < lg->vector.stops.size(); i++) {\r
558 alpha *= lg->vector.stops[i].opacity;\r
559 }\r
560 \r
561 if (alpha != 1.0 || _pushed_alphas.back() != 1.0) {\r
562 PdfObject *pdf_gstate = pdf_file->begin_resource(pdf_extgstate);\r
563 *pdf_gstate << "<< /Type /ExtGState\n";\r
564 \r
565 if (_pushed_alphas.back() != 1.0) {\r
566 *pdf_gstate << " /ca " << _pushed_alphas.back() << "\n";\r
567 *pdf_gstate << " /AIS false\n";\r
568 }\r
569 \r
570 if (alpha != 1.0) {\r
571 PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_shading);\r
572 PdfObject *pdf_smask = pdf_file->begin_resource(pdf_none);\r
573 PdfObject *pdf_xobj = pdf_file->begin_resource(pdf_none);\r
574 \r
575 \r
576 *pdf_gstate << " /SMask " << pdf_smask->get_id() << " 0 R\n";\r
577 \r
578 \r
579 *pdf_alpha << "<<\n/ShadingType 2\n/ColorSpace /DeviceGray\n";\r
580 *pdf_alpha << "/Coords [" << p1[NR::X] << " " << p1[NR::Y] << " " << p2[NR::X] << " " << p2[NR::Y] <<"]\n";\r
581 *pdf_alpha << "/Extend [true true]\n";\r
582 *pdf_alpha << "/Domain [0 1]\n";\r
583 *pdf_alpha << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
584 \r
585 for (gint i = 0; unsigned(i) < lg->vector.stops.size() - 1; i++) {\r
586 *pdf_alpha << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
587 *pdf_alpha << "/C0 [" << lg->vector.stops[i].opacity << "]\n";\r
588 *pdf_alpha << "/C1 [" << lg->vector.stops[i+1].opacity << "]\n";\r
589 *pdf_alpha << "/N 1\n>>\n";\r
590 }\r
591 *pdf_alpha << "]\n/Domain [0 1]\n";\r
592 *pdf_alpha << "/Bounds [ ";\r
593 for (gint i=0;unsigned(i)<lg->vector.stops.size()-2;i++) {\r
594 *pdf_alpha << lg->vector.stops[i+1].offset <<" ";\r
595 }\r
596 *pdf_alpha << "]\n";\r
597 *pdf_alpha << "/Encode [ ";\r
598 for (gint i=0;unsigned(i)<lg->vector.stops.size()-1;i++) {\r
599 *pdf_alpha << "0 1 ";\r
600 }\r
601 *pdf_alpha << "]\n";\r
602 *pdf_alpha << ">>\n>>\n";\r
603 \r
604 \r
605 *pdf_smask << "<< /Type /Mask\n";\r
606 *pdf_smask << " /S /Alpha\n";\r
607 *pdf_smask << " /BC [0.0]\n";\r
608 *pdf_smask << " /G " << pdf_xobj->get_id() << " 0 R\n";\r
609 *pdf_smask << ">>\n";\r
610 \r
611 \r
612 *pdf_xobj << "<< /Type /XObject\n";\r
613 *pdf_xobj << " /Subtype /Form\n";\r
614 *pdf_xobj << " /FormType 1\n";\r
615 *pdf_xobj << " /BBox ["\r
616 << pbox->x0 << " "\r
617 << pbox->y0 << " "\r
618 << pbox->x1 << " "\r
619 << pbox->y1 << "]\n";\r
620 *pdf_xobj << " /Group\n";\r
621 *pdf_xobj << " << /Type /Group\n";\r
622 *pdf_xobj << " /S /Transparency\n";\r
623 *pdf_xobj << " /CS /DeviceGray \n";\r
624 *pdf_xobj << " >>\n";\r
625 *pdf_xobj << " /Resources\n";\r
626 *pdf_xobj << " <<\n";\r
627 *pdf_xobj << " /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n";\r
628 *pdf_xobj << " /Shading << "\r
629 << pdf_alpha->get_name() << " "\r
630 << pdf_alpha->get_id() << " 0 R >>\n";\r
631 *pdf_xobj << " >>\n";\r
632 \r
633 Inkscape::SVGOStringStream os_tmp;\r
634 os_tmp.setf(std::ios::fixed);\r
635 \r
636 os_tmp << "q\n"\r
637 << pbox->x0 << " " << pbox->y0 << " m\n"\r
638 << pbox->x1 << " " << pbox->y0 << " l\n"\r
639 << pbox->x1 << " " << pbox->y1 << " l\n"\r
640 << pbox->x0 << " " << pbox->y1 << " l\n"\r
641 << pbox->x0 << " " << pbox->y0 << " l\n"\r
642 << "h\n"\r
643 << "W* n\n";\r
644 \r
645 SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
646 if (g->gradientTransform_set) {\r
647 os_tmp << g->gradientTransform[0] << " "\r
648 << g->gradientTransform[1] << " "\r
649 << g->gradientTransform[2] << " "\r
650 << g->gradientTransform[3] << " "\r
651 << g->gradientTransform[4] << " "\r
652 << g->gradientTransform[5] << " cm\n"; \r
653 }\r
654 \r
655 os_tmp << pdf_alpha->get_name() << " sh\n";\r
656 os_tmp << "Q\n";\r
657 *pdf_xobj << " /Length " << os_tmp.str().length() << "\n";\r
658 *pdf_xobj << ">>\n";\r
659 *pdf_xobj << "stream\n";\r
660 *pdf_xobj << os_tmp.str().c_str();\r
661 *pdf_xobj << "endstream\n";\r
662 \r
663 \r
664 pdf_file->end_resource(pdf_alpha);\r
665 pdf_file->end_resource(pdf_smask);\r
666 pdf_file->end_resource(pdf_xobj);\r
667 }\r
668 \r
669 *pdf_gstate << ">>\n";\r
670 \r
671 os << pdf_gstate->get_name() << " gs\n";\r
672 \r
673 pdf_file->end_resource(pdf_gstate);\r
674 }\r
675 } else if (SP_IS_RADIALGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
676 \r
677 SPRadialGradient *rg=SP_RADIALGRADIENT(SP_STYLE_FILL_SERVER (style));\r
678 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built\r
679 \r
680 NR::Point c (rg->cx.computed, rg->cy.computed);\r
681 NR::Point f (rg->fx.computed, rg->fy.computed);\r
682 double r = rg->r.computed;\r
683 \r
684 NR::Coord const df = hypot(f[NR::X] - c[NR::X], f[NR::Y] - c[NR::Y]);\r
685 if (df >= r) {\r
686 f[NR::X] = c[NR::X] + (f[NR::X] - c[NR::X] ) * r / (float) df;\r
687 f[NR::Y] = c[NR::Y] + (f[NR::Y] - c[NR::Y] ) * r / (float) df;\r
688 }\r
689 \r
690 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
691 // convert to userspace\r
692 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
693 c *= bbox2user;\r
694 f *= bbox2user;\r
695 r *= bbox2user.expansion();\r
696 }\r
697 \r
698 double alpha = 1.0;\r
699 for (gint i = 0; unsigned(i) < rg->vector.stops.size(); i++) {\r
700 alpha *= rg->vector.stops[i].opacity;\r
701 }\r
702 \r
703 if (alpha != 1.0 || _pushed_alphas.back() != 1.0) {\r
704 PdfObject *pdf_gstate = pdf_file->begin_resource(pdf_extgstate);\r
705 *pdf_gstate << "<< /Type /ExtGState\n";\r
706 \r
707 if (_pushed_alphas.back() != 1.0) {\r
708 *pdf_gstate << " /ca " << _pushed_alphas.back() << "\n";\r
709 *pdf_gstate << " /AIS false\n";\r
710 }\r
711 \r
712 if (alpha != 1.0) {\r
713 PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_shading);\r
714 PdfObject *pdf_smask = pdf_file->begin_resource(pdf_none);\r
715 PdfObject *pdf_xobj = pdf_file->begin_resource(pdf_none);\r
716 \r
717 \r
718 *pdf_gstate << " /SMask " << pdf_smask->get_id() << " 0 R\n";\r
719 \r
720 \r
721 *pdf_alpha << "<<\n/ShadingType 3\n/ColorSpace /DeviceGray\n";\r
722 *pdf_alpha << "/Coords ["<< f[NR::X] <<" "<< f[NR::Y] <<" 0 "<< c[NR::X] <<" "<< c[NR::Y] <<" "<< r <<"]\n";\r
723 *pdf_alpha << "/Extend [true true]\n";\r
724 *pdf_alpha << "/Domain [0 1]\n";\r
725 *pdf_alpha << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
726 \r
727 for (gint i = 0; unsigned(i) < rg->vector.stops.size() - 1; i++) {\r
728 *pdf_alpha << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
729 *pdf_alpha << "/C0 [" << rg->vector.stops[i].opacity << "]\n";\r
730 *pdf_alpha << "/C1 [" << rg->vector.stops[i+1].opacity << "]\n";\r
731 *pdf_alpha << "/N 1\n>>\n";\r
732 }\r
733 *pdf_alpha << "]\n/Domain [0 1]\n";\r
734 *pdf_alpha << "/Bounds [ ";\r
735 \r
736 for (gint i=0;unsigned(i)<rg->vector.stops.size()-2;i++) {\r
737 *pdf_alpha << rg->vector.stops[i+1].offset <<" ";\r
738 }\r
739 *pdf_alpha << "]\n";\r
740 *pdf_alpha << "/Encode [ ";\r
741 for (gint i=0;unsigned(i)<rg->vector.stops.size()-1;i++) {\r
742 *pdf_alpha << "0 1 ";\r
743 }\r
744 *pdf_alpha << "]\n";\r
745 *pdf_alpha << ">>\n>>\n";\r
746 \r
747 \r
748 *pdf_smask << "<< /Type /Mask\n";\r
749 *pdf_smask << " /S /Alpha\n";\r
750 *pdf_smask << " /BC [0.0]\n";\r
751 *pdf_smask << " /G " << pdf_xobj->get_id() << " 0 R\n";\r
752 *pdf_smask << ">>\n";\r
753 \r
754 \r
755 *pdf_xobj << "<< /Type /XObject\n";\r
756 *pdf_xobj << " /Subtype /Form\n";\r
757 *pdf_xobj << " /FormType 1\n";\r
758 *pdf_xobj << " /BBox ["\r
759 << pbox->x0 << " "\r
760 << pbox->y0 << " "\r
761 << pbox->x1 << " "\r
762 << pbox->y1 << "]\n";\r
763 *pdf_xobj << " /Group\n";\r
764 *pdf_xobj << " << /Type /Group\n";\r
765 *pdf_xobj << " /S /Transparency\n";\r
766 *pdf_xobj << " /CS /DeviceGray \n";\r
767 *pdf_xobj << " >>\n";\r
768 *pdf_xobj << " /Resources\n";\r
769 *pdf_xobj << " <<\n";\r
770 *pdf_xobj << " /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n";\r
771 *pdf_xobj << " /Shading << "\r
772 << pdf_alpha->get_name() << " "\r
773 << pdf_alpha->get_id() << " 0 R >>\n";\r
774 *pdf_xobj << " >>\n";\r
775 \r
776 Inkscape::SVGOStringStream os_tmp;\r
777 os_tmp.setf(std::ios::fixed);\r
778 \r
779 os_tmp << "q\n"\r
780 << pbox->x0 << " " << pbox->y0 << " m\n"\r
781 << pbox->x1 << " " << pbox->y0 << " l\n"\r
782 << pbox->x1 << " " << pbox->y1 << " l\n"\r
783 << pbox->x0 << " " << pbox->y1 << " l\n"\r
784 << pbox->x0 << " " << pbox->y0 << " l\n"\r
785 << "h\n"\r
786 << "W* n\n";\r
787 \r
788 SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
789 if (g->gradientTransform_set) {\r
790 os_tmp << g->gradientTransform[0] << " "\r
791 << g->gradientTransform[1] << " "\r
792 << g->gradientTransform[2] << " "\r
793 << g->gradientTransform[3] << " "\r
794 << g->gradientTransform[4] << " "\r
795 << g->gradientTransform[5] << " cm\n"; \r
796 }\r
797 \r
798 os_tmp << pdf_alpha->get_name() << " sh\n";\r
799 os_tmp << "Q\n";\r
800 *pdf_xobj << " /Length " << os_tmp.str().length() << "\n";\r
801 *pdf_xobj << ">>\n";\r
802 *pdf_xobj << "stream\n";\r
803 *pdf_xobj << os_tmp.str().c_str();\r
804 *pdf_xobj << "endstream\n";\r
805 \r
806 \r
807 pdf_file->end_resource(pdf_alpha);\r
808 pdf_file->end_resource(pdf_smask);\r
809 pdf_file->end_resource(pdf_xobj);\r
810 }\r
811 \r
812 *pdf_gstate << ">>\n";\r
813 \r
814 os << pdf_gstate->get_name() << " gs\n";\r
815 \r
816 pdf_file->end_resource(pdf_gstate);\r
817 }\r
818 }\r
819 }\r
820 }\r
821 \r
822 void\r
823 PrintPDF::print_fill_style(SVGOStringStream &os, SPStyle const *const style, NRRect const *pbox)\r
824 {\r
825 g_return_if_fail( style->fill.type == SP_PAINT_TYPE_COLOR\r
826 || ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
827 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) ) );\r
828 \r
829 if (style->fill.type == SP_PAINT_TYPE_COLOR) {\r
830 float rgb[3];\r
831 sp_color_get_rgb_floatv(&style->fill.value.color, rgb);\r
832 \r
833 os << rgb[0] << " " << rgb[1] << " " << rgb[2] << " rg\n";\r
834 } else {\r
835 g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
836 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
837 \r
838 if (SP_IS_LINEARGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
839 \r
840 SPLinearGradient *lg=SP_LINEARGRADIENT(SP_STYLE_FILL_SERVER (style));\r
841 sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built\r
842 \r
843 NR::Point p1 (lg->x1.computed, lg->y1.computed);\r
844 NR::Point p2 (lg->x2.computed, lg->y2.computed);\r
845 if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
846 // convert to userspace\r
847 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
848 p1 *= bbox2user;\r
849 p2 *= bbox2user;\r
850 }\r
851 \r
852 PdfObject *pdf_shade = pdf_file->begin_resource(pdf_shading);\r
853 \r
854 *pdf_shade << "<<\n/ShadingType 2\n/ColorSpace /DeviceRGB\n";\r
855 *pdf_shade << "/Coords [" << p1[NR::X] << " " << p1[NR::Y] << " " << p2[NR::X] << " " << p2[NR::Y] <<"]\n";\r
856 *pdf_shade << "/Extend [true true]\n";\r
857 *pdf_shade << "/Domain [0 1]\n";\r
858 *pdf_shade << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
859 \r
860 for (gint i = 0; unsigned(i) < lg->vector.stops.size() - 1; i++) {\r
861 float rgb[3];\r
862 sp_color_get_rgb_floatv(&lg->vector.stops[i].color, rgb);\r
863 *pdf_shade << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
864 *pdf_shade << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
865 sp_color_get_rgb_floatv(&lg->vector.stops[i+1].color, rgb);\r
866 *pdf_shade << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
867 *pdf_shade << "/N 1\n>>\n";\r
868 }\r
869 *pdf_shade << "]\n/Domain [0 1]\n";\r
870 *pdf_shade << "/Bounds [ ";\r
871 for (gint i=0;unsigned(i)<lg->vector.stops.size()-2;i++) {\r
872 *pdf_shade << lg->vector.stops[i+1].offset <<" ";\r
873 }\r
874 *pdf_shade << "]\n";\r
875 *pdf_shade << "/Encode [ ";\r
876 for (gint i=0;unsigned(i)<lg->vector.stops.size()-1;i++) {\r
877 *pdf_shade << "0 1 ";\r
878 }\r
879 *pdf_shade << "]\n";\r
880 *pdf_shade << ">>\n>>\n";\r
881 os << pdf_shade->get_name() << " ";\r
882 \r
883 pdf_file->end_resource(pdf_shade);\r
884 } else if (SP_IS_RADIALGRADIENT (SP_STYLE_FILL_SERVER (style))) {\r
885 \r
886 SPRadialGradient *rg=SP_RADIALGRADIENT(SP_STYLE_FILL_SERVER (style));\r
887 sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built\r
888 \r
889 NR::Point c (rg->cx.computed, rg->cy.computed);\r
890 NR::Point f (rg->fx.computed, rg->fy.computed);\r
891 double r = rg->r.computed;\r
892 \r
893 NR::Coord const df = hypot(f[NR::X] - c[NR::X], f[NR::Y] - c[NR::Y]);\r
894 if (df >= r) {\r
895 f[NR::X] = c[NR::X] + (f[NR::X] - c[NR::X] ) * r / (float) df;\r
896 f[NR::Y] = c[NR::Y] + (f[NR::Y] - c[NR::Y] ) * r / (float) df;\r
897 }\r
898 \r
899 if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {\r
900 // convert to userspace\r
901 NR::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0);\r
902 c *= bbox2user;\r
903 f *= bbox2user;\r
904 r *= bbox2user.expansion();\r
905 }\r
906 \r
907 PdfObject *pdf_shade = pdf_file->begin_resource(pdf_shading);\r
908 \r
909 *pdf_shade << "<<\n/ShadingType 3\n/ColorSpace /DeviceRGB\n";\r
910 *pdf_shade << "/Coords ["<< f[NR::X] <<" "<< f[NR::Y] <<" 0 "<< c[NR::X] <<" "<< c[NR::Y] <<" "<< r <<"]\n";\r
911 *pdf_shade << "/Extend [true true]\n";\r
912 *pdf_shade << "/Domain [0 1]\n";\r
913 *pdf_shade << "/Function <<\n/FunctionType 3\n/Functions\n[\n";\r
914 \r
915 for (gint i = 0; unsigned(i) < rg->vector.stops.size() - 1; i++) {\r
916 float rgb[3];\r
917 sp_color_get_rgb_floatv(&rg->vector.stops[i].color, rgb);\r
918 *pdf_shade << "<<\n/FunctionType 2\n/Domain [0 1]\n";\r
919 *pdf_shade << "/C0 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
920 sp_color_get_rgb_floatv(&rg->vector.stops[i+1].color, rgb);\r
921 *pdf_shade << "/C1 [" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "]\n";\r
922 *pdf_shade << "/N 1\n>>\n";\r
923 }\r
924 *pdf_shade << "]\n/Domain [0 1]\n";\r
925 *pdf_shade << "/Bounds [ ";\r
926 \r
927 for (gint i=0;unsigned(i)<rg->vector.stops.size()-2;i++) {\r
928 *pdf_shade << rg->vector.stops[i+1].offset <<" ";\r
929 }\r
930 *pdf_shade << "]\n";\r
931 *pdf_shade << "/Encode [ ";\r
932 for (gint i=0;unsigned(i)<rg->vector.stops.size()-1;i++) {\r
933 *pdf_shade << "0 1 ";\r
934 }\r
935 *pdf_shade << "]\n";\r
936 *pdf_shade << ">>\n>>\n";\r
937 \r
938 os << pdf_shade->get_name() << " ";\r
939 \r
940 pdf_file->end_resource(pdf_shade);\r
941 }\r
942 }\r
943 }\r
944 \r
945 void\r
946 PrintPDF::print_stroke_style(SVGOStringStream &os, SPStyle const *style)\r
947 {\r
948 float rgb[3];\r
949 \r
950 sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);\r
951 os << rgb[0] << " " << rgb[1] << " " << rgb[2] << " RG\n";\r
952 \r
953 float alpha = 1.0;\r
954 alpha *= SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);\r
955 alpha *= _pushed_alphas.back();\r
956 \r
957 if (alpha != 1.0) {\r
958 PdfObject *pdf_alpha = pdf_file->begin_resource(pdf_extgstate);\r
959 *pdf_alpha << "<< /Type /ExtGState\n";\r
960 *pdf_alpha << " /CA " << alpha << "\n";\r
961 *pdf_alpha << " /AIS false\n";\r
962 *pdf_alpha << ">>\n";\r
963 \r
964 os << pdf_alpha->get_name() << " gs\n";\r
965 \r
966 pdf_file->end_resource(pdf_alpha);\r
967 }\r
968 \r
969 // There are rare cases in which for a solid line stroke_dasharray_set is true. To avoid\r
970 // invalid PS-lines such as "[0.0000000 0.0000000] 0.0000000 setdash", which should be "[] 0 setdash",\r
971 // we first check if all components of stroke_dash.dash are 0.\r
972 bool LineSolid = true;\r
973 if (style->stroke_dash.n_dash &&\r
974 style->stroke_dash.dash )\r
975 {\r
976 int i = 0;\r
977 while (LineSolid && (i < style->stroke_dash.n_dash)) {\r
978 if (style->stroke_dash.dash[i] > 0.00000001)\r
979 LineSolid = false;\r
980 i++;\r
981 }\r
982 if (!LineSolid) {\r
983 os << "[";\r
984 for (i = 0; i < style->stroke_dash.n_dash; i++) {\r
985 if (i > 0) {\r
986 os << " ";\r
987 }\r
988 os << style->stroke_dash.dash[i];\r
989 }\r
990 os << "] " << style->stroke_dash.offset << " d\n";\r
991 } else {\r
992 os << "[] 0 d\n";\r
993 }\r
994 } else {\r
995 os << "[] 0 d\n";\r
996 }\r
997 \r
998 os << style->stroke_width.computed << " w\n";\r
999 os << style->stroke_linejoin.computed << " j\n";\r
1000 os << style->stroke_linecap.computed << " J\n";\r
1001 os <<\r
1002 ( style->stroke_miterlimit.value > 1 ?\r
1003 style->stroke_miterlimit.value : 1 ) << " M\n";\r
1004 }\r
1005 \r
1006 \r
1007 unsigned int\r
1008 PrintPDF::fill(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *const style,\r
1009 NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)\r
1010 {\r
1011 Inkscape::SVGOStringStream os;\r
1012 os.setf(std::ios::fixed);\r
1013 \r
1014 if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
1015 if (_bitmap) return 0;\r
1016 \r
1017 if ( style->fill.type == SP_PAINT_TYPE_COLOR ) {\r
1018 os << "q\n";\r
1019 print_fill_style(os, style, pbox);\r
1020 print_fill_alpha(os, style, pbox);\r
1021 print_bpath(os, bpath->path);\r
1022 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {\r
1023 os << "f*\n";\r
1024 } else {\r
1025 os << "f\n";\r
1026 }\r
1027 os << "Q\n";\r
1028 }\r
1029 else if ( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1030 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) )\r
1031 {\r
1032 os << "q\n";\r
1033 print_bpath(os, bpath->path);\r
1034 \r
1035 if (style->fill_rule.value == SP_WIND_RULE_EVENODD) {\r
1036 g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1037 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
1038 SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
1039 os << "W* n\n";\r
1040 print_fill_alpha(os, style, pbox);\r
1041 if (g->gradientTransform_set) {\r
1042 os << "q\n";\r
1043 os << g->gradientTransform[0] << " "\r
1044 << g->gradientTransform[1] << " "\r
1045 << g->gradientTransform[2] << " "\r
1046 << g->gradientTransform[3] << " "\r
1047 << g->gradientTransform[4] << " "\r
1048 << g->gradientTransform[5] << " cm\n";\r
1049 }\r
1050 print_fill_style(os, style, pbox);\r
1051 os << "sh\n";\r
1052 if (g->gradientTransform_set) {\r
1053 os << "Q\n";\r
1054 }\r
1055 } else {\r
1056 g_assert( style->fill.type == SP_PAINT_TYPE_PAINTSERVER\r
1057 && SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)) );\r
1058 SPGradient const *g = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));\r
1059 os << "W n\n";\r
1060 print_fill_alpha(os, style, pbox);\r
1061 if (g->gradientTransform_set) {\r
1062 os << "q\n";\r
1063 os << g->gradientTransform[0] << " "\r
1064 << g->gradientTransform[1] << " "\r
1065 << g->gradientTransform[2] << " "\r
1066 << g->gradientTransform[3] << " "\r
1067 << g->gradientTransform[4] << " "\r
1068 << g->gradientTransform[5] << " cm\n"; \r
1069 }\r
1070 print_fill_style(os, style, pbox);\r
1071 os << "sh\n";\r
1072 if (g->gradientTransform_set) {\r
1073 os << "Q\n";\r
1074 }\r
1075 }\r
1076 \r
1077 os << "Q\n";\r
1078 } \r
1079 \r
1080 pdf_file->puts(os);\r
1081 return 0;\r
1082 }\r
1083 \r
1084 \r
1085 unsigned int\r
1086 PrintPDF::stroke(Inkscape::Extension::Print *mod, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style,\r
1087 NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)\r
1088 {\r
1089 if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
1090 if (_bitmap) return 0;\r
1091 \r
1092 if (style->stroke.type == SP_PAINT_TYPE_COLOR) {\r
1093 Inkscape::SVGOStringStream os;\r
1094 os.setf(std::ios::fixed);\r
1095 \r
1096 os << "q\n";\r
1097 \r
1098 print_stroke_style(os, style);\r
1099 print_bpath(os, bpath->path);\r
1100 os << "S\n";\r
1101 \r
1102 os << "Q\n";\r
1103 \r
1104 pdf_file->puts(os);\r
1105 }\r
1106 \r
1107 return 0;\r
1108 }\r
1109 \r
1110 unsigned int\r
1111 PrintPDF::image(Inkscape::Extension::Print *mod, guchar *px, unsigned int w, unsigned int h, unsigned int rs,\r
1112 NRMatrix const *transform, SPStyle const *style)\r
1113 {\r
1114 if (!_stream) return 0; // XXX: fixme, returning -1 as unsigned.\r
1115 if (_bitmap) return 0;\r
1116 \r
1117 return print_image(_stream, px, w, h, rs, transform);\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 os << "h\n";\r
1245 }\r
1246 closed = true;\r
1247 os << bp->x3 << " " << bp->y3 << " m\n";\r
1248 break;\r
1249 case NR_MOVETO_OPEN:\r
1250 if (closed) {\r
1251 os << "h\n";\r
1252 }\r
1253 closed = false;\r
1254 os << bp->x3 << " " << bp->y3 << " m\n";\r
1255 break;\r
1256 case NR_LINETO:\r
1257 os << bp->x3 << " " << bp->y3 << " l\n";\r
1258 break;\r
1259 case NR_CURVETO:\r
1260 os << 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 os << "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 PdfObject *pdf_image = pdf_file->begin_resource(pdf_xobject);\r
1443 PdfObject *pdf_image_len = pdf_file->begin_resource(pdf_none);\r
1444 \r
1445 PdfObject *pdf_smask = pdf_file->begin_resource(pdf_xobject);\r
1446 PdfObject *pdf_smask_len = pdf_file->begin_resource(pdf_none);\r
1447 \r
1448 \r
1449 os << "q\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] << " cm\n";\r
1456 os << pdf_image->get_name() << " Do\n";\r
1457 os << "Q\n";\r
1458 pdf_file->puts(os);\r
1459 \r
1460 \r
1461 *pdf_image << "<< /Type /XObject\n";\r
1462 *pdf_image << " /Subtype /Image\n";\r
1463 *pdf_image << " /Width " << width << "\n";\r
1464 *pdf_image << " /Height " << height << "\n";\r
1465 *pdf_image << " /ColorSpace /DeviceRGB\n";\r
1466 *pdf_image << " /BitsPerComponent 8\n";\r
1467 *pdf_image << " /Length " << pdf_image_len->get_id() << " 0 R\n";\r
1468 *pdf_image << " /Filter /ASCIIHexDecode\n";\r
1469 *pdf_image << " /SMask " << pdf_smask->get_id() << " 0 R\n";\r
1470 *pdf_image << ">>\n";\r
1471 \r
1472 *pdf_image << "stream\n";\r
1473 \r
1474 \r
1475 *pdf_smask << "<< /Type /XObject\n";\r
1476 *pdf_smask << " /Subtype /Image\n";\r
1477 *pdf_smask << " /Width " << width << "\n";\r
1478 *pdf_smask << " /Height " << height << "\n";\r
1479 *pdf_smask << " /ColorSpace /DeviceGray\n";\r
1480 *pdf_smask << " /BitsPerComponent 8\n";\r
1481 *pdf_smask << " /Length " << pdf_smask_len->get_id() << " 0 R\n";\r
1482 *pdf_smask << " /Filter /ASCIIHexDecode\n";\r
1483 *pdf_smask << ">>\n";\r
1484 \r
1485 *pdf_smask << "stream\n";\r
1486 \r
1487 \r
1488 unsigned long image_len = pdf_image->get_length();\r
1489 unsigned long smask_len = pdf_smask->get_length();\r
1490 \r
1491 int image_chars = 0;\r
1492 int smask_chars = 0;\r
1493 \r
1494 for (unsigned i = 0; i < height; i++) {\r
1495 guchar const *const src = px + i * rs;\r
1496 \r
1497 for (unsigned j = 0; j < width; j++) {\r
1498 char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\r
1499 guchar const *src_ptr = src + 4*j;\r
1500 \r
1501 /* Iterate over RGB */\r
1502 for (int rgb = 0; rgb < 3; rgb++) {\r
1503 guchar val = *(src_ptr + rgb);\r
1504 \r
1505 *pdf_image << hex[val / 16];\r
1506 *pdf_image << hex[val % 16];\r
1507 \r
1508 image_chars += 2;\r
1509 if (image_chars >= 78) {\r
1510 *pdf_image << "\n";\r
1511 image_chars = 0;\r
1512 }\r
1513 }\r
1514 \r
1515 guchar alpha = *(src_ptr + 3);\r
1516 \r
1517 *pdf_smask << hex[alpha / 16];\r
1518 *pdf_smask << hex[alpha % 16];\r
1519 \r
1520 smask_chars += 2;\r
1521 if (smask_chars >= 78) {\r
1522 *pdf_smask << "\n";\r
1523 smask_chars = 0;\r
1524 }\r
1525 }\r
1526 }\r
1527 \r
1528 \r
1529 *pdf_image << ">\n";\r
1530 image_len = pdf_image->get_length() - image_len;\r
1531 \r
1532 *pdf_image << "endstream\n";\r
1533 pdf_file->end_resource(pdf_image);\r
1534 \r
1535 *pdf_image_len << image_len << "\n";\r
1536 pdf_file->end_resource(pdf_image_len);\r
1537 \r
1538 \r
1539 *pdf_smask << ">\n";\r
1540 smask_len = pdf_smask->get_length() - smask_len;\r
1541 \r
1542 *pdf_smask << "endstream\n";\r
1543 pdf_file->end_resource(pdf_smask);\r
1544 \r
1545 *pdf_smask_len << smask_len << "\n";\r
1546 pdf_file->end_resource(pdf_smask_len);\r
1547 \r
1548 \r
1549 return 0;\r
1550 }\r
1551 \r
1552 bool\r
1553 PrintPDF::textToPath(Inkscape::Extension::Print * ext)\r
1554 {\r
1555 return ext->get_param_bool("textToPath");\r
1556 }\r
1557 \r
1558 #include "clear-n_.h"\r
1559 \r
1560 void\r
1561 PrintPDF::init(void)\r
1562 {\r
1563 /* PDF print */\r
1564 (void) Inkscape::Extension::build_from_mem(\r
1565 "<inkscape-extension>\n"\r
1566 "<name>" N_("PDF Print") "</name>\n"\r
1567 "<id>" SP_MODULE_KEY_PRINT_PDF "</id>\n"\r
1568 "<param name=\"bitmap\" type=\"boolean\">FALSE</param>\n"\r
1569 "<param name=\"resolution\" type=\"string\">72</param>\n"\r
1570 "<param name=\"destination\" type=\"string\">| lp</param>\n"\r
1571 "<param name=\"pageBoundingBox\" type=\"boolean\">TRUE</param>\n"\r
1572 "<param name=\"textToPath\" type=\"boolean\">TRUE</param>\n"\r
1573 "<print/>\n"\r
1574 "</inkscape-extension>", new PrintPDF());\r
1575 }\r
1576 \r
1577 \r
1578 } /* namespace Internal */\r
1579 } /* namespace Extension */\r
1580 } /* namespace Inkscape */\r
1581 \r
1582 /* End of GNU GPL code */\r
1583 \r
1584 \r
1585 /*\r
1586 Local Variables:\r
1587 mode:c++\r
1588 c-file-style:"stroustrup"\r
1589 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
1590 indent-tabs-mode:nil\r
1591 fill-column:99\r
1592 End:\r
1593 */\r
1594 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r