Code

fix build again for old poppler libs, by reverting a part of my previous commit
[inkscape.git] / src / svg / svg-color.cpp
1 #define __SP_SVG_COLOR_C__
3 /**
4  * \file
5  * Reading \& writing of SVG/CSS colors.
6  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *
11  * Copyright (C) 1999-2002 Lauris Kaplinski
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <cstdlib>
21 #include <cstdio> // sprintf
22 #include <cstring>
23 #include <string>
24 #include <cassert>
25 #include <math.h>
26 #include <glib/gmem.h>
27 #include <glib.h> // g_assert
28 #include <glib/gmessages.h>
29 #include <glib/gstrfuncs.h>
30 #include <glib/ghash.h>
31 #include <glib/gutils.h>
32 #include <errno.h>
34 #include "strneq.h"
35 #include "preferences.h"
36 #include "svg-color.h"
37 #include "svg-icc-color.h"
38 #include "svg-device-color.h"
40 #if ENABLE_LCMS
41 #include <lcms.h>
42 #include "color.h"
43 #include "color-profile.h"
44 #include "document.h"
45 #include "inkscape.h"
46 #include "profile-manager.h"
47 #endif // ENABLE_LCMS
49 using std::sprintf;
51 struct SPSVGColor {
52     unsigned long rgb;
53     char const *name;
54 };
56 /*
57  * These are the colors defined in the SVG standard
58  */
59 static SPSVGColor const sp_svg_color_named[] = {
60     { 0xF0F8FF, "aliceblue" },
61     { 0xFAEBD7, "antiquewhite" },
62     { 0x00FFFF, "aqua" },
63     { 0x7FFFD4, "aquamarine" },
64     { 0xF0FFFF, "azure" },
65     { 0xF5F5DC, "beige" },
66     { 0xFFE4C4, "bisque" },
67     { 0x000000, "black" },
68     { 0xFFEBCD, "blanchedalmond" },
69     { 0x0000FF, "blue" },
70     { 0x8A2BE2, "blueviolet" },
71     { 0xA52A2A, "brown" },
72     { 0xDEB887, "burlywood" },
73     { 0x5F9EA0, "cadetblue" },
74     { 0x7FFF00, "chartreuse" },
75     { 0xD2691E, "chocolate" },
76     { 0xFF7F50, "coral" },
77     { 0x6495ED, "cornflowerblue" },
78     { 0xFFF8DC, "cornsilk" },
79     { 0xDC143C, "crimson" },
80     { 0x00FFFF, "cyan" },
81     { 0x00008B, "darkblue" },
82     { 0x008B8B, "darkcyan" },
83     { 0xB8860B, "darkgoldenrod" },
84     { 0xA9A9A9, "darkgray" },
85     { 0x006400, "darkgreen" },
86     { 0xA9A9A9, "darkgrey" },
87     { 0xBDB76B, "darkkhaki" },
88     { 0x8B008B, "darkmagenta" },
89     { 0x556B2F, "darkolivegreen" },
90     { 0xFF8C00, "darkorange" },
91     { 0x9932CC, "darkorchid" },
92     { 0x8B0000, "darkred" },
93     { 0xE9967A, "darksalmon" },
94     { 0x8FBC8F, "darkseagreen" },
95     { 0x483D8B, "darkslateblue" },
96     { 0x2F4F4F, "darkslategray" },
97     { 0x2F4F4F, "darkslategrey" },
98     { 0x00CED1, "darkturquoise" },
99     { 0x9400D3, "darkviolet" },
100     { 0xFF1493, "deeppink" },
101     { 0x00BFFF, "deepskyblue" },
102     { 0x696969, "dimgray" },
103     { 0x696969, "dimgrey" },
104     { 0x1E90FF, "dodgerblue" },
105     { 0xB22222, "firebrick" },
106     { 0xFFFAF0, "floralwhite" },
107     { 0x228B22, "forestgreen" },
108     { 0xFF00FF, "fuchsia" },
109     { 0xDCDCDC, "gainsboro" },
110     { 0xF8F8FF, "ghostwhite" },
111     { 0xFFD700, "gold" },
112     { 0xDAA520, "goldenrod" },
113     { 0x808080, "gray" },
114     { 0x808080, "grey" },
115     { 0x008000, "green" },
116     { 0xADFF2F, "greenyellow" },
117     { 0xF0FFF0, "honeydew" },
118     { 0xFF69B4, "hotpink" },
119     { 0xCD5C5C, "indianred" },
120     { 0x4B0082, "indigo" },
121     { 0xFFFFF0, "ivory" },
122     { 0xF0E68C, "khaki" },
123     { 0xE6E6FA, "lavender" },
124     { 0xFFF0F5, "lavenderblush" },
125     { 0x7CFC00, "lawngreen" },
126     { 0xFFFACD, "lemonchiffon" },
127     { 0xADD8E6, "lightblue" },
128     { 0xF08080, "lightcoral" },
129     { 0xE0FFFF, "lightcyan" },
130     { 0xFAFAD2, "lightgoldenrodyellow" },
131     { 0xD3D3D3, "lightgray" },
132     { 0x90EE90, "lightgreen" },
133     { 0xD3D3D3, "lightgrey" },
134     { 0xFFB6C1, "lightpink" },
135     { 0xFFA07A, "lightsalmon" },
136     { 0x20B2AA, "lightseagreen" },
137     { 0x87CEFA, "lightskyblue" },
138     { 0x778899, "lightslategray" },
139     { 0x778899, "lightslategrey" },
140     { 0xB0C4DE, "lightsteelblue" },
141     { 0xFFFFE0, "lightyellow" },
142     { 0x00FF00, "lime" },
143     { 0x32CD32, "limegreen" },
144     { 0xFAF0E6, "linen" },
145     { 0xFF00FF, "magenta" },
146     { 0x800000, "maroon" },
147     { 0x66CDAA, "mediumaquamarine" },
148     { 0x0000CD, "mediumblue" },
149     { 0xBA55D3, "mediumorchid" },
150     { 0x9370DB, "mediumpurple" },
151     { 0x3CB371, "mediumseagreen" },
152     { 0x7B68EE, "mediumslateblue" },
153     { 0x00FA9A, "mediumspringgreen" },
154     { 0x48D1CC, "mediumturquoise" },
155     { 0xC71585, "mediumvioletred" },
156     { 0x191970, "midnightblue" },
157     { 0xF5FFFA, "mintcream" },
158     { 0xFFE4E1, "mistyrose" },
159     { 0xFFE4B5, "moccasin" },
160     { 0xFFDEAD, "navajowhite" },
161     { 0x000080, "navy" },
162     { 0xFDF5E6, "oldlace" },
163     { 0x808000, "olive" },
164     { 0x6B8E23, "olivedrab" },
165     { 0xFFA500, "orange" },
166     { 0xFF4500, "orangered" },
167     { 0xDA70D6, "orchid" },
168     { 0xEEE8AA, "palegoldenrod" },
169     { 0x98FB98, "palegreen" },
170     { 0xAFEEEE, "paleturquoise" },
171     { 0xDB7093, "palevioletred" },
172     { 0xFFEFD5, "papayawhip" },
173     { 0xFFDAB9, "peachpuff" },
174     { 0xCD853F, "peru" },
175     { 0xFFC0CB, "pink" },
176     { 0xDDA0DD, "plum" },
177     { 0xB0E0E6, "powderblue" },
178     { 0x800080, "purple" },
179     { 0xFF0000, "red" },
180     { 0xBC8F8F, "rosybrown" },
181     { 0x4169E1, "royalblue" },
182     { 0x8B4513, "saddlebrown" },
183     { 0xFA8072, "salmon" },
184     { 0xF4A460, "sandybrown" },
185     { 0x2E8B57, "seagreen" },
186     { 0xFFF5EE, "seashell" },
187     { 0xA0522D, "sienna" },
188     { 0xC0C0C0, "silver" },
189     { 0x87CEEB, "skyblue" },
190     { 0x6A5ACD, "slateblue" },
191     { 0x708090, "slategray" },
192     { 0x708090, "slategrey" },
193     { 0xFFFAFA, "snow" },
194     { 0x00FF7F, "springgreen" },
195     { 0x4682B4, "steelblue" },
196     { 0xD2B48C, "tan" },
197     { 0x008080, "teal" },
198     { 0xD8BFD8, "thistle" },
199     { 0xFF6347, "tomato" },
200     { 0x40E0D0, "turquoise" },
201     { 0xEE82EE, "violet" },
202     { 0xF5DEB3, "wheat" },
203     { 0xFFFFFF, "white" },
204     { 0xF5F5F5, "whitesmoke" },
205     { 0xFFFF00, "yellow" },
206     { 0x9ACD32, "yellowgreen" }
207 };
209 static GHashTable *sp_svg_create_color_hash();
211 guint32
212 sp_svg_read_color(gchar const *str, guint32 const dfl)
214     return sp_svg_read_color(str, NULL, dfl);
217 static guint32
218 internal_sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def)
220     static GHashTable *colors = NULL;
221     guint32 val = 0;
223     if (str == NULL) return def;
224     while ((*str <= ' ') && *str) str++;
225     if (!*str) return def;
227     if (str[0] == '#') {
228         gint i;
229         for (i = 1; str[i]; i++) {
230             int hexval;
231             if (str[i] >= '0' && str[i] <= '9')
232                 hexval = str[i] - '0';
233             else if (str[i] >= 'A' && str[i] <= 'F')
234                 hexval = str[i] - 'A' + 10;
235             else if (str[i] >= 'a' && str[i] <= 'f')
236                 hexval = str[i] - 'a' + 10;
237             else
238                 break;
239             val = (val << 4) + hexval;
240         }
241         /* handle #rgb case */
242         if (i == 1 + 3) {
243             val = ((val & 0xf00) << 8) |
244                 ((val & 0x0f0) << 4) |
245                 (val & 0x00f);
246             val |= val << 4;
247         } else if (i != 1 + 6) {
248             /* must be either 3 or 6 digits. */
249             return def;
250         }
251         if (end_ptr) {
252             *end_ptr = str + i;
253         }
254     } else if (strneq(str, "rgb(", 4)) {
255         bool hasp, hasd;
256         gchar *s, *e;
257         gdouble r, g, b;
259         s = (gchar *) str + 4;
260         hasp = false;
261         hasd = false;
263         r = g_ascii_strtod(s, &e);
264         if (s == e) return def;
265         s = e;
266         if (*s == '%') {
267             hasp = true;
268             s += 1;
269         } else {
270             hasd = true;
271         }
272         while (*s && g_ascii_isspace(*s)) s += 1;
273         if (*s != ',') return def;
274         s += 1;
275         while (*s && g_ascii_isspace(*s)) s += 1;
276         g = g_ascii_strtod(s, &e);
277         if (s == e) return def;
278         s = e;
279         if (*s == '%') {
280             hasp = true;
281             s += 1;
282         } else {
283             hasd = true;
284         }
285         while (*s && g_ascii_isspace(*s)) s += 1;
286         if (*s != ',') return def;
287         s += 1;
288         while (*s && g_ascii_isspace(*s)) s += 1;
289         b = g_ascii_strtod(s, &e);
290         if (s == e) return def;
291         s = e;
292         if (*s == '%') {
293             hasp = true;
294             s += 1;
295         } else {
296             hasd = true;
297         }
298         while(*s && g_ascii_isspace(*s)) s += 1;
299         if (*s != ')') {
300             return def;
301         }
302         ++s;
303         if (hasp && hasd) return def;
304         if (hasp) {
305             val = (guint) floor(CLAMP(r, 0.0, 100.0) * 2.559999) << 24;
306             val |= ((guint) floor(CLAMP(g, 0.0, 100.0) * 2.559999) << 16);
307             val |= ((guint) floor(CLAMP(b, 0.0, 100.0) * 2.559999) << 8);
308         } else {
309             val = (guint) CLAMP(r, 0, 255) << 24;
310             val |= ((guint) CLAMP(g, 0, 255) << 16);
311             val |= ((guint) CLAMP(b, 0, 255) << 8);
312         }
313         if (end_ptr) {
314             *end_ptr = s;
315         }
316         return val;
317     } else {
318         gint i;
319         if (!colors) {
320             colors = sp_svg_create_color_hash();
321         }
322         gchar c[32];
323         for (i = 0; i < 31; i++) {
324             if (str[i] == ';' || g_ascii_isspace(str[i])) {
325                 c[i] = '\0';
326                 break;
327             }
328             c[i] = g_ascii_tolower(str[i]);
329             if (!str[i]) break;
330         }
331         c[31] = '\0';
333         gpointer const rgb_ptr = g_hash_table_lookup(colors, c);
334         if (rgb_ptr) {
335             val = *(static_cast<unsigned long *>(rgb_ptr));
336         } else {
337             return def;
338         }
339         if (end_ptr) {
340             *end_ptr = str + i;
341         }
342     }
344     return (val << 8);
347 guint32
348 sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 dfl)
350     /* I've been rather hurried in editing the above to add support for end_ptr, so I'm adding
351      * this check wrapper. */
352     gchar const *end = str;
353     guint32 const ret = internal_sp_svg_read_color(str, &end, dfl);
354     assert(((ret == dfl) && (end == str))
355            || (((ret & 0xff) == 0)
356                && (str < end)));
357     if (str < end) {
358         gchar *buf = (gchar *) g_malloc(end + 1 - str);
359         memcpy(buf, str, end - str);
360         buf[end - str] = '\0';
361         gchar const *buf_end = buf;
362         guint32 const check = internal_sp_svg_read_color(buf, &buf_end, 1);
363         assert(check == ret
364                && buf_end - buf == end - str);
365         g_free(buf);
367         if ( end_ptr ) {
368             *end_ptr = end;
369         }
370     }
371     return ret;
375 /**
376  * Converts an RGB colour expressed in form 0x00rrggbb to a CSS/SVG representation of that colour.
377  * The result is valid even in SVG Tiny or non-SVG CSS.
378  */
379 static void
380 rgb24_to_css(char *const buf, unsigned const rgb24)
382     assert(rgb24 < (1u << 24));
384     /* SVG 1.1 Full allows additional colour names not supported by SVG Tiny, but we don't bother
385      * with them: it's good for these colours to be copyable to non-SVG CSS stylesheets and for
386      * documents to be more viewable in SVG Tiny/Basic viewers; and some of the SVG Full names are
387      * less meaningful than hex equivalents anyway.  And it's easier for a person to map from the
388      * restricted set because the only component values are {00,80,ff}, other than silver 0xc0c0c0.
389      */
391     char const *src = NULL;
392     switch (rgb24) {
393         /* Extracted mechanically from the table at
394          * http://www.w3.org/TR/REC-html40/types.html#h-6.5 .*/
395         case 0x000000: src = "black"; break;            
396         case 0xc0c0c0: src = "silver"; break;           
397         case 0x808080: src = "gray"; break;             
398         case 0xffffff: src = "white"; break;            
399         case 0x800000: src = "maroon"; break;           
400         case 0xff0000: src = "red"; break;              
401         case 0x800080: src = "purple"; break;           
402         case 0xff00ff: src = "fuchsia"; break;          
403         case 0x008000: src = "green"; break; 
404         case 0x00ff00: src = "lime"; break;  
405         case 0x808000: src = "olive"; break; 
406         case 0xffff00: src = "yellow"; break;
407         case 0x000080: src = "navy"; break;  
408         case 0x0000ff: src = "blue"; break;  
409         case 0x008080: src = "teal"; break;  
410         case 0x00ffff: src = "aqua"; break;  
412         default: {
413             if ((rgb24 & 0xf0f0f) * 0x11 == rgb24) {
414                 /* Can use the shorter three-digit form #rgb instead of #rrggbb. */
415                 sprintf(buf, "#%x%x%x",
416                         (rgb24 >> 16) & 0xf,
417                         (rgb24 >> 8) & 0xf,
418                         rgb24 & 0xf);
419             } else {
420                 sprintf(buf, "#%06x", rgb24);
421             }
422             break;
423         }
424     }
425     if (src) {
426         strcpy(buf, src);
427     }
429     // assert(sp_svg_read_color(buf, 0xff) == (rgb24 << 8));
432 /**
433  * Converts an RGBA32 colour to a CSS/SVG representation of the RGB portion of that colour.  The
434  * result is valid even in SVG Tiny or non-SVG CSS.
435  *
436  * \param rgba32 Colour expressed in form 0xrrggbbaa.
437  * \pre buflen \>= 8.
438  */
439 void
440 sp_svg_write_color(gchar *buf, unsigned const buflen, guint32 const rgba32)
442     g_assert(8 <= buflen);
444     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
445     unsigned const rgb24 = rgba32 >> 8;
446     if (prefs->getBool("/options/svgoutput/usenamedcolors")) {
447         rgb24_to_css(buf, rgb24);
448     } else {
449         g_snprintf(buf, buflen, "#%06x", rgb24);
450     }
453 static GHashTable *
454 sp_svg_create_color_hash()
456     GHashTable *colors = g_hash_table_new(g_str_hash, g_str_equal);
458     for (unsigned i = 0 ; i < G_N_ELEMENTS(sp_svg_color_named) ; i++) {
459         g_hash_table_insert(colors,
460                             (gpointer)(sp_svg_color_named[i].name),
461                             (gpointer)(&sp_svg_color_named[i].rgb));
462     }
464     return colors;
467 //helper function borrowed from src/widgets/sp-color-icc-selector.cpp:
468 void getThings( DWORD space, gchar const**& namers, gchar const**& tippies, guint const*& scalies );
470 void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b){
471     guchar color_out[4];
472     guchar color_in[4];
473     if (icc){
474 g_message("profile name: %s", icc->colorProfile.c_str());
475         Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(icc->colorProfile.c_str());
476         if ( prof ) {
477             cmsHTRANSFORM trans = prof->getTransfToSRGB8();
478             if ( trans ) {
479                 gchar const** names = 0;
480                 gchar const** tips = 0;
481                 guint const* scales = 0;
482                 getThings( prof->getColorSpace(), names, tips, scales );
484                 guint count = _cmsChannelsOf( prof->getColorSpace() );
485                 if (count>4) count=4; //do we need it? Should we allow an arbitrary number of color values? Or should we limit to a maximum? (max==4?)
486                 for (guint i=0;i<count; i++){
487                     color_in[i] = (guchar) ((((gdouble)icc->colors[i])*256.0) * (gdouble)scales[i]);
488 g_message("input[%d]: %d",i, color_in[i]);
489                 }
491                 cmsDoTransform( trans, color_in, color_out, 1 );
492 g_message("transform to sRGB done");
493             }
494             *r = color_out[0];
495             *g = color_out[1];
496             *b = color_out[2];
497         }
498     }
501 /*
502  * Some discussion at http://markmail.org/message/bhfvdfptt25kgtmj
503  * Allowed ASCII first characters:  ':', 'A'-'Z', '_', 'a'-'z'
504  * Allowed ASCII remaining chars add: '-', '.', '0'-'9',
505  */
506 bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest )
508     bool good = true;
510     if ( end_ptr ) {
511         *end_ptr = str;
512     }
513     if ( dest ) {
514         dest->colorProfile.clear();
515         dest->colors.clear();
516     }
518     if ( !str ) {
519         // invalid input
520         good = false;
521     } else {
522         while ( g_ascii_isspace(*str) ) {
523             str++;
524         }
526         good = strneq( str, "icc-color(", 10 );
528         if ( good ) {
529             str += 10;
530             while ( g_ascii_isspace(*str) ) {
531                 str++;
532             }
534             if ( !g_ascii_isalpha(*str)
535                  && ( !(0x080 & *str) )
536                  && (*str != '_')
537                  && (*str != ':') ) {
538                 // Name must start with a certain type of character
539                 good = false;
540             } else {
541                 while ( g_ascii_isdigit(*str) || g_ascii_isalpha(*str)
542                         || (*str == '-') || (*str == ':') || (*str == '_') || (*str == '.') ) {
543                     if ( dest ) {
544                         dest->colorProfile += *str;
545                     }
546                     str++;
547                 }
548                 while ( g_ascii_isspace(*str) || *str == ',' ) {
549                     str++;
550                 }
551             }
552         }
554         if ( good ) {
555             while ( *str && *str != ')' ) {
556                 if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') {
557                     gchar* endPtr = 0;
558                     gdouble dbl = g_ascii_strtod( str, &endPtr );
559                     if ( !errno ) {
560                         if ( dest ) {
561                             dest->colors.push_back( dbl );
562                         }
563                         str = endPtr;
564                     } else {
565                         good = false;
566                         break;
567                     }
569                     while ( g_ascii_isspace(*str) || *str == ',' ) {
570                         str++;
571                     }
572                 } else {
573                     break;
574                 }
575             }
576         }
578         // We need to have ended on a closing parenthesis
579         if ( good ) {
580             while ( g_ascii_isspace(*str) ) {
581                 str++;
582             }
583             good &= (*str == ')');
584         }
585     }
587     if ( good ) {
588         if ( end_ptr ) {
589             *end_ptr = str;
590         }
591     } else {
592         if ( dest ) {
593             dest->colorProfile.clear();
594             dest->colors.clear();
595         }
596     }
598     return good;
602 bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest )
604     return sp_svg_read_icc_color(str, NULL, dest);
607 bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest)
609     bool good = true;
610     unsigned int max_colors;
612     if ( end_ptr ) {
613         *end_ptr = str;
614     }
615     if ( dest ) {
616         dest->colors.clear();
617     }
619     if ( !str ) {
620         // invalid input
621         good = false;
622     } else {
623         while ( g_ascii_isspace(*str) ) {
624             str++;
625         }
627         dest->type = DEVICE_COLOR_INVALID;
628         if (strneq( str, "device-gray(", 12 )){
629                 dest->type = DEVICE_GRAY;
630                 max_colors=1;
631                 str += 12;
632         }
634         if (strneq( str, "device-rgb(", 11 )){
635                 dest->type = DEVICE_RGB;
636                 max_colors=3;
637                 str += 11;
638         }
640         if (strneq( str, "device-cmyk(", 12 )){
641                 dest->type = DEVICE_CMYK;
642                 max_colors=4;
643                 str += 12;
644         }
646         if (strneq( str, "device-nchannel(", 16 )){
647                 dest->type = DEVICE_NCHANNEL;
648                 max_colors=0;
649                 str += 16;
650         }
652         if ( dest->type != DEVICE_COLOR_INVALID ) {
653             while ( g_ascii_isspace(*str) ) {
654                 str++;
655             }
657             while ( *str && *str != ')' ) {
658                 if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') {
659                     gchar* endPtr = 0;
660                     gdouble dbl = g_ascii_strtod( str, &endPtr );
661                     if ( !errno ) {
662                         if ( dest ) {
663                             dest->colors.push_back( dbl );
664                         }
665                         str = endPtr;
666                     } else {
667                         good = false;
668                         break;
669                     }
671                     while ( g_ascii_isspace(*str) || *str == ',' ) {
672                         str++;
673                     }
674                 } else {
675                     break;
676                 }
677             }
678         }
680         // We need to have ended on a closing parenthesis
681         if ( good ) {
682             while ( g_ascii_isspace(*str) ) {
683                 str++;
684             }
685             good &= (*str == ')');
686         }
687     }
689     if ( dest->colors.size() == 0) good=false;
690     if ( dest->type != DEVICE_NCHANNEL && (dest->colors.size() != max_colors)) good=false;
692     if ( good ) {
693         if ( end_ptr ) {
694             *end_ptr = str;
695         }
696     } else {
697         if ( dest ) {
698             dest->type = DEVICE_COLOR_INVALID;
699             dest->colors.clear();
700         }
701     }
703     return good;
707 bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest)
709     return sp_svg_read_device_color(str, NULL, dest);
712 /*
713   Local Variables:
714   mode:c++
715   c-file-style:"stroustrup"
716   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
717   indent-tabs-mode:nil
718   fill-column:99
719   End:
720 */
721 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :