Code

ef97da444a9b09c39353d1f40a89fe8b5bf63fab
[inkscape.git] / src / extension / internal / pdflatex-renderer.cpp
1 #define EXTENSION_INTERNAL_PDF_LATEX_RENDERER_CPP
3 /** \file
4  * Rendering LaTeX file (pdf+latex output)
5  */
6 /*
7  * Authors:
8  *   Johan Engelen <goejendaagh@zonnet.nl>
9  *   Miklos Erdelyi <erdelyim@gmail.com>
10  *
11  * Copyright (C) 2006-2010 Authors
12  *
13  * Most of the pre- and postamble is copied from GNUPlot's epslatex terminal output :-)
14  *
15  * Licensed under GNU GPL
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #ifndef PANGO_ENABLE_BACKEND
23 #define PANGO_ENABLE_BACKEND
24 #endif
26 #ifndef PANGO_ENABLE_ENGINE
27 #define PANGO_ENABLE_ENGINE
28 #endif
31 #include <signal.h>
32 #include <errno.h>
34 #include "libnr/nr-rect.h"
35 #include "libnrtype/Layout-TNG.h"
36 #include <2geom/transforms.h>
37 #include <2geom/pathvector.h>
39 #include <glib/gmem.h>
41 #include <glibmm/i18n.h>
42 #include "display/nr-arena.h"
43 #include "display/nr-arena-item.h"
44 #include "display/nr-arena-group.h"
45 #include "display/curve.h"
46 #include "display/canvas-bpath.h"
47 #include "sp-item.h"
48 #include "sp-item-group.h"
49 #include "style.h"
50 #include "marker.h"
51 #include "sp-linear-gradient.h"
52 #include "sp-radial-gradient.h"
53 #include "sp-root.h"
54 #include "sp-use.h"
55 #include "sp-text.h"
56 #include "sp-flowtext.h"
57 #include "sp-mask.h"
58 #include "sp-clippath.h"
59 #include "text-editing.h"
61 #include <unit-constants.h>
62 #include "helper/png-write.h"
63 #include "helper/pixbuf-ops.h"
65 #include "pdflatex-renderer.h"
66 #include "extension/system.h"
68 #include "io/sys.h"
70 #include <cairo.h>
72 // include support for only the compiled-in surface types
73 #ifdef CAIRO_HAS_PDF_SURFACE
74 #include <cairo-pdf.h>
75 #endif
76 #ifdef CAIRO_HAS_PS_SURFACE
77 #include <cairo-ps.h>
78 #endif
80 //#define TRACE(_args) g_message(_args)
81 #define TRACE(_args)
82 //#define TEST(_args) _args
83 #define TEST(_args)
85 // FIXME: expose these from sp-clippath/mask.cpp
86 struct SPClipPathView {
87     SPClipPathView *next;
88     unsigned int key;
89     NRArenaItem *arenaitem;
90     NRRect bbox;
91 };
93 struct SPMaskView {
94     SPMaskView *next;
95     unsigned int key;
96     NRArenaItem *arenaitem;
97     NRRect bbox;
98 };
100 namespace Inkscape {
101 namespace Extension {
102 namespace Internal {
105 PDFLaTeXRenderer::PDFLaTeXRenderer(void)
106   : _stream(NULL),
107     _filename(NULL),
108     _width(0),
109     _height(0)
111     push_transform(Geom::identity());
114 PDFLaTeXRenderer::~PDFLaTeXRenderer(void)
116     if (_stream) {
117         writePostamble();
119         fclose(_stream);
120     }
122     /* restore default signal handling for SIGPIPE */
123 #if !defined(_WIN32) && !defined(__WIN32__)
124     (void) signal(SIGPIPE, SIG_DFL);
125 #endif
127     if (_filename) {
128         g_free(_filename);
129     }
131     return;
134 /** This should create the output LaTeX file, and assign it to _stream.
135  * @return Returns true when succesfull
136  */
137 bool
138 PDFLaTeXRenderer::setTargetFile(gchar const *filename) {
139     if (filename != NULL) {
140         while (isspace(*filename)) filename += 1;
141         
142         _filename = g_strdup(filename);
144         gchar *filename_ext = g_strdup_printf("%s.tex", filename);
145         Inkscape::IO::dump_fopen_call(filename_ext, "K");
146         FILE *osf = Inkscape::IO::fopen_utf8name(filename_ext, "w+");
147         if (!osf) {
148             fprintf(stderr, "inkscape: fopen(%s): %s\n",
149                     filename_ext, strerror(errno));
150             return false;
151         }
152         _stream = osf;
153         g_free(filename_ext);
154     }
156     if (_stream) {
157         /* fixme: this is kinda icky */
158 #if !defined(_WIN32) && !defined(__WIN32__)
159         (void) signal(SIGPIPE, SIG_IGN);
160 #endif
161     }
163     fprintf(_stream, "%%%% Creator: Inkscape %s, www.inkscape.org\n", PACKAGE_STRING);
164     fprintf(_stream, "%%%% PDF + LaTeX output extension by Johan Engelen, 2010\n");
165     fprintf(_stream, "%%%% Accompanies %s.pdf\n", _filename);
166     /* flush this to test output stream as early as possible */
167     if (fflush(_stream)) {
168         if (ferror(_stream)) {
169             g_print("Error %d on LaTeX file output stream: %s\n", errno,
170                     g_strerror(errno));
171         }
172         g_print("Output to LaTeX file failed\n");
173         /* fixme: should use pclose() for pipes */
174         fclose(_stream);
175         _stream = NULL;
176         fflush(stdout);
177         return false;
178     }
180     writePreamble();
182     return true;
185 /* Most of this preamble is copied from GNUPlot's epslatex terminal output :-) */
186 static char const preamble[] =
187 "\\begingroup                                                                              \n"
188 "  \\makeatletter                                                                          \n"
189 "  \\providecommand\\color[2][]{%%                                                         \n"
190 "    \\GenericError{(gnuplot) \\space\\space\\space\\@spaces}{%%                           \n"
191 "      Package color not loaded in conjunction with                                        \n"
192 "      terminal option `colourtext'%%                                                      \n"
193 "    }{See the gnuplot documentation for explanation.%%                                    \n"
194 "    }{Either use 'blacktext' in gnuplot or load the package                               \n"
195 "      color.sty in LaTeX.}%%                                                              \n"
196 "    \\renewcommand\\color[2][]{}%%                                                        \n"
197 "  }%%                                                                                     \n"
198 "  \\providecommand\\includegraphics[2][]{%%                                               \n"
199 "    \\GenericError{(gnuplot) \\space\\space\\space\\@spaces}{%%                           \n"
200 "      Package graphicx or graphics not loaded%%                                           \n"
201 "    }{See the gnuplot documentation for explanation.%%                                    \n"
202 "    }{The gnuplot epslatex terminal needs graphicx.sty or graphics.sty.}%%                \n"
203 "    \\renewcommand\\includegraphics[2][]{}%%                                              \n"
204 "  }%%                                                                                     \n"
205 "  \\providecommand\\rotatebox[2]{#2}%%                                                    \n"
206 "  \\@ifundefined{ifGPcolor}{%%                                                            \n"
207 "    \\newif\\ifGPcolor                                                                    \n"
208 "    \\GPcolorfalse                                                                        \n"
209 "  }{}%%                                                                                   \n"
210 "  \\@ifundefined{ifGPblacktext}{%%                                                        \n"
211 "    \\newif\\ifGPblacktext                                                                \n"
212 "    \\GPblacktexttrue                                                                     \n"
213 "  }{}%%                                                                                   \n"
214 "  %% define a \\g@addto@macro without @ in the name:                                      \n"
215 "  \\let\\gplgaddtomacro\\g@addto@macro                                                    \n"
216 "  %% define empty templates for all commands taking text:                                 \n"
217 "  \\gdef\\gplbacktext{}%%                                                                 \n"
218 "  \\gdef\\gplfronttext{}%%                                                                \n"
219 "  \\makeatother                                                                           \n"
220 "  \\ifGPblacktext                                                                         \n"
221 "    %% no textcolor at all                                                                \n"
222 "    \\def\\colorrgb#1{}%%                                                                 \n"
223 "    \\def\\colorgray#1{}%%                                                                \n"
224 "  \\else                                                                                  \n"
225 "    %% gray or color?                                                                     \n"
226 "    \\ifGPcolor                                                                           \n"
227 "      \\def\\colorrgb#1{\\color[rgb]{#1}}%%                                               \n"
228 "      \\def\\colorgray#1{\\color[gray]{#1}}%%                                             \n"
229 "      \\expandafter\\def\\csname LTw\\endcsname{\\color{white}}%%                         \n"
230 "      \\expandafter\\def\\csname LTb\\endcsname{\\color{black}}%%                         \n"
231 "      \\expandafter\\def\\csname LTa\\endcsname{\\color{black}}%%                         \n"
232 "      \\expandafter\\def\\csname LT0\\endcsname{\\color[rgb]{1,0,0}}%%                    \n"
233 "      \\expandafter\\def\\csname LT1\\endcsname{\\color[rgb]{0,1,0}}%%                    \n"
234 "      \\expandafter\\def\\csname LT2\\endcsname{\\color[rgb]{0,0,1}}%%                    \n"
235 "      \\expandafter\\def\\csname LT3\\endcsname{\\color[rgb]{1,0,1}}%%                    \n"
236 "      \\expandafter\\def\\csname LT4\\endcsname{\\color[rgb]{0,1,1}}%%                    \n"
237 "      \\expandafter\\def\\csname LT5\\endcsname{\\color[rgb]{1,1,0}}%%                    \n"
238 "      \\expandafter\\def\\csname LT6\\endcsname{\\color[rgb]{0,0,0}}%%                    \n"
239 "      \\expandafter\\def\\csname LT7\\endcsname{\\color[rgb]{1,0.3,0}}%%                  \n"
240 "      \\expandafter\\def\\csname LT8\\endcsname{\\color[rgb]{0.5,0.5,0.5}}%%              \n"
241 "    \\else                                                                                \n"
242 "      %% gray                                                                             \n"
243 "      \\def\\colorrgb#1{\\color{black}}%%                                                 \n"
244 "      \\def\\colorgray#1{\\color[gray]{#1}}%%                                             \n"
245 "      \\expandafter\\def\\csname LTw\\endcsname{\\color{white}}%                          \n"
246 "      \\expandafter\\def\\csname LTb\\endcsname{\\color{black}}%                          \n"
247 "      \\expandafter\\def\\csname LTa\\endcsname{\\color{black}}%                          \n"
248 "      \\expandafter\\def\\csname LT0\\endcsname{\\color{black}}%                          \n"
249 "      \\expandafter\\def\\csname LT1\\endcsname{\\color{black}}%                          \n"
250 "      \\expandafter\\def\\csname LT2\\endcsname{\\color{black}}%                          \n"
251 "      \\expandafter\\def\\csname LT3\\endcsname{\\color{black}}%                          \n"
252 "      \\expandafter\\def\\csname LT4\\endcsname{\\color{black}}%                          \n"
253 "      \\expandafter\\def\\csname LT5\\endcsname{\\color{black}}%                          \n"
254 "      \\expandafter\\def\\csname LT6\\endcsname{\\color{black}}%                          \n"
255 "      \\expandafter\\def\\csname LT7\\endcsname{\\color{black}}%                          \n"
256 "      \\expandafter\\def\\csname LT8\\endcsname{\\color{black}}%                          \n"
257 "    \\fi                                                                                  \n"
258 "  \\fi                                                                                    \n"
259 "  \\setlength{\\unitlength}{1pt}%                                                         \n";
261 static char const postamble1[] =
262 "    }%%                                                                                    \n"
263 "    \\gplgaddtomacro\\gplfronttext{%                                                       \n"
264 "    }%%                                                                                    \n"
265 "    \\gplbacktext                                                                          \n";
267 static char const postamble2[] =
268 "    \\gplfronttext                                                                         \n"
269 "  \\end{picture}%                                                                          \n"
270 "\\endgroup                                                                                 \n";
272 void
273 PDFLaTeXRenderer::writePreamble()
275     fprintf(_stream, "%s", preamble);
277 void
278 PDFLaTeXRenderer::writePostamble()
280     fprintf(_stream, "%s", postamble1);
282     // strip pathname on windows, as it is probably desired. It is not possible to work without paths on windows yet. (bug)
283 #ifdef WIN32
284     gchar *figurefile = g_path_get_basename(_filename);
285 #else
286     gchar *figurefile = g_strdup(_filename);
287 #endif
288     fprintf(_stream, "      \\put(0,0){\\includegraphics{%s.pdf}}%%\n", figurefile);
289     g_free(figurefile);
291     fprintf(_stream, "%s", postamble2);
294 void
295 PDFLaTeXRenderer::sp_group_render(SPItem *item)
297     SPGroup *group = SP_GROUP(item);
299     GSList *l = g_slist_reverse(group->childList(false));
300     while (l) {
301         SPObject *o = SP_OBJECT (l->data);
302         if (SP_IS_ITEM(o)) {
303             renderItem (SP_ITEM (o));
304         }
305         l = g_slist_remove (l, o);
306     }
309 void
310 PDFLaTeXRenderer::sp_use_render(SPItem *item)
312 /*
313     bool translated = false;
314     SPUse *use = SP_USE(item);
316     if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) {
317         Geom::Matrix tp(Geom::Translate(use->x.computed, use->y.computed));
318         ctx->pushState();
319         ctx->transform(&tp);
320         translated = true;
321     }
323     if (use->child && SP_IS_ITEM(use->child)) {
324         renderItem(SP_ITEM(use->child));
325     }
327     if (translated) {
328         ctx->popState();
329     }
330 */
333 void
334 PDFLaTeXRenderer::sp_text_render(SPItem *item)
336     SPText *textobj = SP_TEXT (item);
338     Geom::Matrix i2doc = sp_item_i2doc_affine(item);
339     push_transform(i2doc);
341     gchar *str = sp_te_get_string_multiline(item);
342     Geom::Point pos = textobj->attributes.firstXY() * transform();
343     gchar *alignment = "lb";
345     // get rotation
346     Geom::Matrix wotransl = i2doc.without_translation();
347     double degrees = -180/M_PI * Geom::atan2(wotransl.xAxis());
349     pop_transform();
351     // write to LaTeX
352     Inkscape::SVGOStringStream os;
354 //    os << "\\put(" << pos[Geom::X] << "," << pos[Geom::Y] << "){\\makebox(0,0)[" << alignment << "]{\\strut{}" << str << "}}%%\n";
355     os << "\\put(" << pos[Geom::X] << "," << pos[Geom::Y] << "){";
356     if (!Geom::are_near(degrees,0.)) {
357         os << "\\rotatebox{" << degrees << "}{";
358     }
359     os <<   str;
360     if (!Geom::are_near(degrees,0.)) {
361         os << "}";
362     }
363     os << "}%%\n";
365     fprintf(_stream, "%s", os.str().c_str());
368 void
369 PDFLaTeXRenderer::sp_flowtext_render(SPItem *item)
371 /*    SPFlowtext *group = SP_FLOWTEXT(item);
373     // write to LaTeX
374     Inkscape::SVGOStringStream os;
376     os << "  \\begin{picture}(" << _width << "," << _height << ")%%\n";
377     os << "    \\gplgaddtomacro\\gplbacktext{%%\n";
378     os << "      \\csname LTb\\endcsname%%\n";
379     os << "\\put(0,0){\\makebox(0,0)[lb]{\\strut{}Position}}%%\n";
381     fprintf(_stream, "%s", os.str().c_str());
382 */
385 void
386 PDFLaTeXRenderer::sp_root_render(SPItem *item)
388     SPRoot *root = SP_ROOT(item);
390 //    ctx->pushState();
391 //    setStateForItem(ctx, item);
392     Geom::Matrix tempmat (root->c2p);
393     push_transform(tempmat);
394     sp_group_render(item);
395     pop_transform();
398 void
399 PDFLaTeXRenderer::sp_item_invoke_render(SPItem *item)
401     // Check item's visibility
402     if (item->isHidden()) {
403         return;
404     }
406     g_message("hier?");
407     if (SP_IS_ROOT(item)) {
408         TRACE(("root\n"));
409         return sp_root_render(item);
410     } else if (SP_IS_GROUP(item)) {
411         TRACE(("group\n"));
412         return sp_group_render(item);
413     } else if (SP_IS_USE(item)) {
414         TRACE(("use begin---\n"));
415         sp_use_render(item);
416         TRACE(("---use end\n"));
417     } else if (SP_IS_TEXT(item)) {
418         TRACE(("text\n"));
419         return sp_text_render(item);
420     } else if (SP_IS_FLOWTEXT(item)) {
421         TRACE(("flowtext\n"));
422         return sp_flowtext_render(item);
423     }
424     // We are not interested in writing the other SPItem types to LaTeX
427 void
428 PDFLaTeXRenderer::setStateForItem(SPItem const *item)
430 /*
431     SPStyle const *style = SP_OBJECT_STYLE(item);
432     ctx->setStateForStyle(style);
434     CairoRenderState *state = ctx->getCurrentState();
435     state->clip_path = item->clip_ref->getObject();
436     state->mask = item->mask_ref->getObject();
437     state->item_transform = Geom::Matrix (item->transform);
439     // If parent_has_userspace is true the parent state's transform
440     // has to be used for the mask's/clippath's context.
441     // This is so because we use the image's/(flow)text's transform for positioning
442     // instead of explicitly specifying it and letting the renderer do the
443     // transformation before rendering the item.
444     if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item) || SP_IS_IMAGE(item))
445         state->parent_has_userspace = TRUE;
446     TRACE(("setStateForItem opacity: %f\n", state->opacity));
447 */
450 void
451 PDFLaTeXRenderer::renderItem(SPItem *item)
453 //    ctx->pushState();
454 //    setStateForItem(ctx, item);
456 //    CairoRenderState *state = ctx->getCurrentState();
457 //    state->need_layer = ( state->mask || state->clip_path || state->opacity != 1.0 );
459     // Draw item on a temporary surface so a mask, clip path, or opacity can be applied to it.
460 //    if (state->need_layer) {
461 //        state->merge_opacity = FALSE;
462 //        ctx->pushLayer();
463 //    }
464     Geom::Matrix tempmat (item->transform);
465 //    ctx->transform(&tempmat);
466     sp_item_invoke_render(item);
468 //    if (state->need_layer)
469 //        ctx->popLayer();
471 //    ctx->popState();
474 bool
475 PDFLaTeXRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base)
477 // The boundingbox calculation here should be exactly the same as the one by CairoRenderer::setupDocument !
479     if (!base)
480         base = SP_ITEM(sp_document_root(doc));
482     NRRect d;
483     if (pageBoundingBox) {
484         d.x0 = d.y0 = 0;
485         d.x1 = ceil(sp_document_width(doc));
486         d.y1 = ceil(sp_document_height(doc));
487     } else {
488         sp_item_invoke_bbox(base, &d, sp_item_i2d_affine(base), TRUE, SPItem::RENDERING_BBOX);
489     }
491     // convert from px to pt
492     push_transform( Geom::Scale(PT_PER_PX, PT_PER_PX) );
494     if (!pageBoundingBox)
495     {
496         double high = sp_document_height(doc);
498         push_transform( Geom::Translate( -d.x0,
499                                          -d.y0 ) );
500     }
502     // flip y-axis
503     push_transform( Geom::Scale(1,-1) * Geom::Translate(0, sp_document_height(doc)) );
505     _width = (d.x1-d.x0) * PT_PER_PX;
506     _height = (d.y1-d.y0) * PT_PER_PX;
508     // write the info to LaTeX
509     Inkscape::SVGOStringStream os;
511     os << "  \\begin{picture}(" << _width << "," << _height << ")%%\n";
512     os << "    \\gplgaddtomacro\\gplbacktext{%%\n";
513     os << "      \\csname LTb\\endcsname%%\n";
515     fprintf(_stream, "%s", os.str().c_str());
517     return true;
520 Geom::Matrix const &
521 PDFLaTeXRenderer::transform()
523     return _transform_stack.top();
526 void
527 PDFLaTeXRenderer::push_transform(Geom::Matrix const &tr)
529     if(_transform_stack.size()){
530         Geom::Matrix tr_top = _transform_stack.top();
531         _transform_stack.push(tr * tr_top);
532     } else {
533         _transform_stack.push(tr);
534     }
537 void
538 PDFLaTeXRenderer::pop_transform()
540     _transform_stack.pop();
543 /*
544 #include "macros.h" // SP_PRINT_*
546 // Apply an SVG clip path
547 void
548 PDFLaTeXRenderer::applyClipPath(CairoRenderContext *ctx, SPClipPath const *cp)
550     g_assert( ctx != NULL && ctx->_is_valid );
552     if (cp == NULL)
553         return;
555     CairoRenderContext::CairoRenderMode saved_mode = ctx->getRenderMode();
556     ctx->setRenderMode(CairoRenderContext::RENDER_MODE_CLIP);
558     Geom::Matrix saved_ctm;
559     if (cp->clipPathUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) {
560         //SP_PRINT_DRECT("clipd", cp->display->bbox);
561         NRRect clip_bbox(cp->display->bbox);
562         Geom::Matrix t(Geom::Scale(clip_bbox.x1 - clip_bbox.x0, clip_bbox.y1 - clip_bbox.y0));
563         t[4] = clip_bbox.x0;
564         t[5] = clip_bbox.y0;
565         t *= ctx->getCurrentState()->transform;
566         ctx->getTransform(&saved_ctm);
567         ctx->setTransform(&t);
568     }
570     TRACE(("BEGIN clip\n"));
571     SPObject *co = SP_OBJECT(cp);
572     for (SPObject *child = sp_object_first_child(co) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
573         if (SP_IS_ITEM(child)) {
574             SPItem *item = SP_ITEM(child);
576             // combine transform of the item in clippath and the item using clippath:
577             Geom::Matrix tempmat (item->transform);
578             tempmat = tempmat * (ctx->getCurrentState()->item_transform);
580             // render this item in clippath
581             ctx->pushState();
582             ctx->transform(&tempmat);
583             setStateForItem(ctx, item);
584             sp_item_invoke_render(item, ctx);
585             ctx->popState();
586         }
587     }
588     TRACE(("END clip\n"));
590     // do clipping only if this was the first call to applyClipPath
591     if (ctx->getClipMode() == CairoRenderContext::CLIP_MODE_PATH
592         && saved_mode == CairoRenderContext::RENDER_MODE_NORMAL)
593         cairo_clip(ctx->_cr);
595     if (cp->clipPathUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX)
596         ctx->setTransform(&saved_ctm);
598     ctx->setRenderMode(saved_mode);
601 // Apply an SVG mask
602 void
603 PDFLaTeXRenderer::applyMask(CairoRenderContext *ctx, SPMask const *mask)
605     g_assert( ctx != NULL && ctx->_is_valid );
607     if (mask == NULL)
608         return;
610     //SP_PRINT_DRECT("maskd", &mask->display->bbox);
611     NRRect mask_bbox(mask->display->bbox);
612     // TODO: should the bbox be transformed if maskUnits != userSpaceOnUse ?
613     if (mask->maskContentUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) {
614         Geom::Matrix t(Geom::Scale(mask_bbox.x1 - mask_bbox.x0, mask_bbox.y1 - mask_bbox.y0));
615         t[4] = mask_bbox.x0;
616         t[5] = mask_bbox.y0;
617         t *= ctx->getCurrentState()->transform;
618         ctx->setTransform(&t);
619     }
621     // Clip mask contents... but...
622     // The mask's bounding box is the "geometric bounding box" which doesn't allow for
623     // filters which extend outside the bounding box. So don't clip.
624     // ctx->addClippingRect(mask_bbox.x0, mask_bbox.y0, mask_bbox.x1 - mask_bbox.x0, mask_bbox.y1 - mask_bbox.y0);
626     ctx->pushState();
628     TRACE(("BEGIN mask\n"));
629     SPObject *co = SP_OBJECT(mask);
630     for (SPObject *child = sp_object_first_child(co) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
631         if (SP_IS_ITEM(child)) {
632             SPItem *item = SP_ITEM(child);
633             renderItem(ctx, item);
634         }
635     }
636     TRACE(("END mask\n"));
638     ctx->popState();
640 */
642 }  /* namespace Internal */
643 }  /* namespace Extension */
644 }  /* namespace Inkscape */
646 #undef TRACE
648 /* End of GNU GPL code */
650 /*
651   Local Variables:
652   mode:c++
653   c-file-style:"stroustrup"
654   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
655   indent-tabs-mode:nil
656   fill-column:99
657   End:
658 */
659 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :