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