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