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