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