root/livinglogic.python.xist/src/ll/xist/csstokenizer.cxx @ 2590:7b6bba35f3a0

Revision 2590:7b6bba35f3a0, 39.5 KB (checked in by Walter Doerwald <walter@…>, 13 years ago)

htmlspecials.pixel() now no longer uses colored pixels, instead color is
done via CSS. The URL for the one remaining transparent pixel can now be
specified via src (either as an XML attribute or via the converter context).

Rename attrs methods with() and without() to withnames() and withoutnames()
(for Python 2.5 compatibility).

Use elinks instead of w3m for asText() and move/rename this method to a
function ll.xist.ns.html.astext().

Try to make XIST independent from PyXML (however PyXML is still required
for parsing via expat and for dtd2xsc.py (because this requires xmlproc)).

Remove the long dperecated method withSep().

Use Py_ssize_t in the C source where appropriate.

Line 
1/*
2** Copyright 1999-2006 by LivingLogic AG, Bayreuth/Germany.
3** Copyright 1999-2006 by Walter Dörwald
4**
5** All Rights Reserved
6**
7** Permission to use, copy, modify, and distribute this software and its documentation
8** for any purpose and without fee is hereby granted, provided that the above copyright
9** notice appears in all copies and that both that copyright notice and this permission
10** notice appear in supporting documentation, and that the name of LivingLogic AG or
11** the author not be used in advertising or publicity pertaining to distribution of the
12** software without specific, written prior permission.
13**
14** LIVINGLOGIC AG AND THE AUTHOR DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15** INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16** LIVINGLOGIC AG OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17** DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
18** IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
19** IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20**
21** This file is based on Java code from the Batik application, which is
22**
23******************************************************************************
24** Copyright (C) The Apache Software Foundation. All rights reserved.        *
25** ------------------------------------------------------------------------- *
26** This software is published under the terms of the Apache Software License *
27** version 1.1, a copy of which has been included at the end of this file    *
28******************************************************************************
29*/
30
31#include "Python.h"
32
33enum Token
34{
35    TOKEN_EOF, /* Represents the EOF lexical unit. */
36    TOKEN_LEFT_CURLY_BRACE, /* Represents the '{' lexical unit. */
37    TOKEN_RIGHT_CURLY_BRACE, /* Represents the '}' lexical unit. */
38    TOKEN_EQUAL, /* Represents the '=' lexical unit. */
39    TOKEN_PLUS, /* Represents the '+' lexical unit. */
40    TOKEN_MINUS, /* Represents the '-' lexical unit. */
41    TOKEN_COMMA, /* Represents the ',' lexical unit. */
42    TOKEN_DOT, /* Represents the '.' lexical unit. */
43    TOKEN_SEMI_COLON, /* Represents the ';' lexical unit. */
44    TOKEN_PRECEDE, /* Represents the '>' lexical unit. */
45    TOKEN_DIVIDE, /* Represents the '/' lexical unit. */
46    TOKEN_LEFT_BRACKET, /* Represents the '[' lexical unit. */
47    TOKEN_RIGHT_BRACKET, /* Represents the ']' lexical unit. */
48    TOKEN_ANY, /* Represents the '*' lexical unit. */
49    TOKEN_LEFT_BRACE, /* Represents the '(' lexical unit. */
50    TOKEN_RIGHT_BRACE, /* Represents the ')' lexical unit. */
51    TOKEN_COLON, /* Represents the ':' lexical unit. */
52    TOKEN_SPACE, /* Represents the white space lexical unit. */
53    TOKEN_COMMENT, /* Represents the comment lexical unit. */
54    TOKEN_STRING, /* Represents the string lexical unit. */
55    TOKEN_IDENTIFIER, /* Represents the identifier lexical unit. */
56    TOKEN_CDO, /* Represents the '<!--' lexical unit. */
57    TOKEN_CDC, /* Represents the '-->' lexical unit. */
58    TOKEN_IMPORTANT_SYMBOL, /* Represents the '!important' lexical unit. */
59    TOKEN_INTEGER, /* Represents an integer. */
60    TOKEN_DASHMATCH, /* Represents the '|=' lexical unit. */
61    TOKEN_INCLUDES, /* Represents the '~=' lexical unit. */
62    TOKEN_HASH, /* Represents the '#name' lexical unit. */
63    TOKEN_IMPORT_SYMBOL, /* Represents the '@import' lexical unit. */
64    TOKEN_AT_KEYWORD, /* Represents the '@ident' lexical unit. */
65    TOKEN_CHARSET_SYMBOL, /* Represents the '@charset' lexical unit. */
66    TOKEN_FONT_FACE_SYMBOL, /* Represents the '@font-face' lexical unit. */
67    TOKEN_MEDIA_SYMBOL, /* Represents the '@media' lexical unit. */
68    TOKEN_PAGE_SYMBOL, /* Represents the '@page' lexical unit. */
69    TOKEN_DIMENSION, /* Represents a dimension lexical unit. */
70    TOKEN_EX, /* Represents a ex lexical unit. */
71    TOKEN_EM, /* Represents a em lexical unit. */
72    TOKEN_CM, /* Represents a cm lexical unit. */
73    TOKEN_MM, /* Represents a mm lexical unit. */
74    TOKEN_IN, /* Represents a in lexical unit. */
75    TOKEN_MS, /* Represents a ms lexical unit. */
76    TOKEN_HZ, /* Represents a hz lexical unit. */
77    TOKEN_PERCENTAGE, /* Represents a % lexical unit. */
78    TOKEN_S, /* Represents a s lexical unit. */
79    TOKEN_PC, /* Represents a pc lexical unit. */
80    TOKEN_PT, /* Represents a pt lexical unit. */
81    TOKEN_PX, /* Represents a px lexical unit. */
82    TOKEN_DEG, /* Represents a deg lexical unit. */
83    TOKEN_RAD, /* Represents a rad lexical unit. */
84    TOKEN_GRAD, /* Represents a grad lexical unit. */
85    TOKEN_KHZ, /* Represents a khz lexical unit. */
86    TOKEN_URI, /* Represents a 'url(URI)' lexical unit. */
87    TOKEN_FUNCTION, /* Represents a 'ident(' lexical unit. */
88    TOKEN_UNICODE_RANGE, /* Represents a unicode range lexical unit. */
89    TOKEN_REAL /* represents a real number. */
90};
91
92const char *token_names[] =
93{
94    "EOF",
95    "LEFT_CURLY_BRACE",
96    "RIGHT_CURLY_BRACE",
97    "EQUAL",
98    "PLUS",
99    "MINUS",
100    "COMMA",
101    "DOT",
102    "SEMI_COLON",
103    "PRECEDE",
104    "DIVIDE",
105    "LEFT_BRACKET",
106    "RIGHT_BRACKET",
107    "ANY",
108    "LEFT_BRACE",
109    "RIGHT_BRACE",
110    "COLON",
111    "SPACE",
112    "COMMENT",
113    "STRING",
114    "IDENTIFIER",
115    "CDO",
116    "CDC",
117    "IMPORTANT_SYMBOL",
118    "INTEGER",
119    "DASHMATCH",
120    "INCLUDES",
121    "HASH",
122    "IMPORT_SYMBOL",
123    "AT_KEYWORD",
124    "CHARSET_SYMBOL",
125    "FONT_FACE_SYMBOL",
126    "MEDIA_SYMBOL",
127    "PAGE_SYMBOL",
128    "DIMENSION",
129    "EX",
130    "EM",
131    "CM",
132    "MM",
133    "IN",
134    "MS",
135    "HZ",
136    "PERCENTAGE",
137    "S",
138    "PC",
139    "PT",
140    "PX",
141    "DEG",
142    "RAD",
143    "GRAD",
144    "KHZ",
145    "URI",
146    "FUNCTION",
147    "UNICODE_RANGE",
148    "REAL"
149};
150
151/* tokenizer type definition */
152class CSSTokenizer
153{
154    public:
155    PyObject_HEAD
156
157    PyObject *string; /* the string to parse */
158
159    /* callbacks */
160    PyObject *startDocument;
161    PyObject *endDocument;
162    PyObject *token;
163};
164
165extern PyTypeObject CSSTokenizer_Type;
166
167static int IDENTIFIER_START[] = { 0, 0, 0x7fffffe, 0x7fffffe }; /* The set of the valid identifier start characters. */
168static int NAME[] = { 0, 67051520, 134217726, 134217726 }; /* The set of the valid name characters. */
169static int STRING[] = { 512, -133, -1, 2147483647 }; /* The set of the valid string characters. */
170static int URI[] = { 0x0, 0xfffffc7a, 0xffffffff, 0x47ffffff }; /* The set of the valid uri characters. */
171
172/* Tests whether the given character is a valid space. */
173static bool isCSSSpace(int c)
174{
175    return ((c==' ') || (c=='\t') || (c=='\n') || (c=='\r') || (c=='\f'));
176}
177
178/* Tests whether the given character is a valid identifier start character. */
179static bool isCSSIdentifierStartCharacter(int c)
180{
181    return c >= 128 || ((IDENTIFIER_START[c / 32] & (1 << (c % 32))) != 0);
182}
183
184/* Tests whether the given character is a valid name character. */
185static bool isCSSNameCharacter(int c)
186{
187    return c >= 128 || ((NAME[c / 32] & (1 << (c % 32))) != 0);
188}
189
190/* Tests whether the given character is a valid hexadecimal character. */
191static bool isCSSHexadecimalCharacter(int c)
192{
193    return isxdigit(c);
194}
195
196/* Tests whether the given character is a valid string character. */
197static bool isCSSStringCharacter(int c) {
198    return c >= 128 || ((STRING[c / 32] & (1 << (c % 32))) != 0);
199}
200
201/* Tests whether the given character is a valid URI character. */
202static bool isCSSURICharacter(int c) {
203    return c >= 128 || ((URI[c / 32] & (1 << (c % 32))) != 0);
204}
205
206class Scanner
207{
208    private:
209    public:
210        const char *buffer;
211        int buflen; /* the size of the buffer */
212        int line; /* The current line. */
213        int column; /* The current column. */
214        int current; /* The current char. */
215        int position; /* The current position in the buffer. */
216        enum Token type; /* The type of the current lexical unit. */
217        int start; /* The start offset of the last lexical unit. */
218        int end; /* The end offset of the last lexical unit. */
219        int blankCharacters; /* The characters to skip to create the string which represents the current token. */
220
221    public:
222        Scanner(const char *s, int ibuflen) :
223            buffer(s),
224            buflen(ibuflen),
225            line(0),
226            column(-1),
227            current('\0'),
228            position(0),
229            type((Token)-1),
230            start(0),
231            end(0),
232            blankCharacters(0)
233        {
234            current = nextChar();
235        }
236
237        int getLine()
238        {
239            return line;
240        }
241
242        int getColumn()
243        {
244            return column;
245        }
246
247        int getStart()
248        {
249            return start;
250        }
251
252        int getEnd()
253        {
254            return end;
255        }
256
257        Token getType()
258        {
259            return type;
260        }
261
262        /* Returns the next token. Return -1 on success, 0 on error */
263        int next()
264        {
265            blankCharacters = 0;
266            start = position - 1;
267            if (!nextToken())
268                return 0;
269            end = position - endGap();
270            return -1;
271        }
272
273    protected:
274        /* Returns the end gap of the current lexical unit. */
275        int endGap()
276        {
277            int result = (current == -1) ? 0 : 1;
278            switch (type)
279            {
280                case TOKEN_FUNCTION:
281                case TOKEN_STRING:
282                case TOKEN_S:
283                case TOKEN_PERCENTAGE:
284                    result += 1;
285                    break;
286                case TOKEN_COMMENT:
287                case TOKEN_HZ:
288                case TOKEN_EM:
289                case TOKEN_EX:
290                case TOKEN_PC:
291                case TOKEN_PT:
292                case TOKEN_PX:
293                case TOKEN_CM:
294                case TOKEN_MM:
295                case TOKEN_IN:
296                case TOKEN_MS:
297                    result += 2;
298                    break;
299                case TOKEN_KHZ:
300                case TOKEN_DEG:
301                case TOKEN_RAD:
302                    result += 3;
303                    break;
304                case TOKEN_GRAD:
305                    result += 4;
306                default:
307                    break;
308            }
309            return result + blankCharacters;
310        }
311
312        /* Sets the value of the current char to the next character or -1 if the end of stream has been reached. */
313        int nextChar()
314        {
315            if (position>=buflen)
316                return current = -1;
317
318            if (current != 10)
319                ++column;
320            else
321            {
322                ++line;
323                column = 0;
324            }
325
326            return current = buffer[position++];
327        }
328
329        /* Scans an escape sequence, if one. Return -1 on success, 0 on error */
330        int escape()
331        {
332            if (isCSSHexadecimalCharacter(current))
333            {
334                nextChar();
335                if (!isCSSHexadecimalCharacter(current))
336                {
337                    if (isCSSSpace(current))
338                    {
339                        nextChar();
340                    }
341                    return -1;
342                }
343                nextChar();
344                if (!isCSSHexadecimalCharacter(current))
345                {
346                    if (isCSSSpace(current))
347                    {
348                        nextChar();
349                    }
350                    return -1;
351                }
352                nextChar();
353                if (!isCSSHexadecimalCharacter(current))
354                {
355                    if (isCSSSpace(current))
356                    {
357                        nextChar();
358                    }
359                    return -1;
360                }
361                nextChar();
362                if (!isCSSHexadecimalCharacter(current))
363                {
364                    if (isCSSSpace(current))
365                    {
366                        nextChar();
367                    }
368                    return -1;
369                }
370                nextChar();
371                if (!isCSSHexadecimalCharacter(current))
372                {
373                    if (isCSSSpace(current))
374                    {
375                        nextChar();
376                    }
377                    return -1;
378                }
379            }
380            if ((current >= ' ' && current <= '~') || current >= 128)
381            {
382                nextChar();
383                return -1;
384            }
385            PyErr_Format(PyExc_ValueError,"character at line %d col %d", getLine(), getColumn());
386            return 0;
387        }
388
389        /* Scans a single quoted string. return -1 on success, 0 on error */
390        int string1()
391        {
392            nextChar();
393            start = position - 1;
394            for (;;)
395            {
396                switch (nextChar())
397                {
398                    case -1:
399                        PyErr_Format(PyExc_ValueError,"eof at line %d column %d", getLine(), getColumn());
400                        return 0;
401                    case '\'':
402                        goto breakloop;
403                    case '"':
404                        break;
405                    case '\\':
406                        switch (nextChar())
407                        {
408                            case '\n':
409                            case '\f':
410                                break;
411                            default:
412                                if (!escape())
413                                    return 0;
414                        }
415                        break;
416                    default:
417                        if (!isCSSStringCharacter(current))
418                        {
419                            PyErr_Format(PyExc_ValueError,"string1 character at line %d column %d", getLine(), getColumn());
420                            return 0;
421                        }
422                }
423            }
424            breakloop:
425            nextChar();
426            type = TOKEN_STRING;
427            return -1;
428        }
429
430        /* Scans a double quoted string. return -1 on success, 0 on error */
431        int string2()
432        {
433            nextChar();
434            start = position - 1;
435            for (;;)
436            {
437                switch (nextChar())
438                {
439                    case -1:
440                        PyErr_Format(PyExc_ValueError,"eof at line %d column %d", getLine(), getColumn());
441                        return 0;
442                    case '\'':
443                        break;
444                    case '"':
445                        goto breakloop;
446                    case '\\':
447                        switch (nextChar())
448                        {
449                            case '\n':
450                            case '\f':
451                                break;
452                            default:
453                                if (!escape())
454                                    return 0;
455                        }
456                        break;
457                    default:
458                        if (!isCSSStringCharacter(current))
459                        {
460                            PyErr_Format(PyExc_ValueError,"string1 character at line %d column %d", getLine(), getColumn());
461                            return 0;
462                        }
463                }
464            }
465            breakloop:
466            nextChar();
467            type = TOKEN_STRING;
468            return -1;
469        }
470
471        /* Scans a number. Return -1 on success, 0 on error */
472        int number()
473        {
474            for (;;)
475            {
476                switch (nextChar())
477                {
478                    case '.':
479                        switch (nextChar())
480                        {
481                            case '0': case '1': case '2': case '3': case '4':
482                            case '5': case '6': case '7': case '8': case '9':
483                            return dotNumber();
484                        }
485                        PyErr_Format(PyExc_ValueError,"number at line %d column %d", getLine(), getColumn());
486                        return 0;
487                    default:
488                        goto breakloop;
489                    case '0': case '1': case '2': case '3': case '4':
490                    case '5': case '6': case '7': case '8': case '9':
491                        break;
492                }
493            }
494            breakloop:
495            return numberUnit(true);
496        }
497
498        /* Scans the decimal part of a number. Return -1 on success, 0 on error */
499        int dotNumber()
500        {
501            for (;;)
502            {
503                switch (nextChar())
504                {
505                    default:
506                        goto breakloop;
507                    case '0': case '1': case '2': case '3': case '4':
508                    case '5': case '6': case '7': case '8': case '9':
509                        break;
510                }
511            }
512            breakloop:
513            return numberUnit(false);
514        }
515
516        /* Scans the unit of a number. Return -1 on success, 0 on error */
517        int numberUnit(bool integer)
518        {
519            switch (current)
520            {
521                case '%':
522                    nextChar();
523                    type = TOKEN_PERCENTAGE;
524                    return -1;
525                case 'c':
526                case 'C':
527                    switch (nextChar())
528                    {
529                        case 'm':
530                        case 'M':
531                            nextChar();
532                            if (current != -1 && isCSSNameCharacter(current))
533                            {
534                                do
535                                {
536                                    nextChar();
537                                } while (current != -1 && isCSSNameCharacter(current));
538                                type = TOKEN_DIMENSION;
539                                return -1;
540                            }
541                            type = TOKEN_CM;
542                            return -1;
543                        default:
544                            while (current != -1 && isCSSNameCharacter(current))
545                            {
546                                nextChar();
547                            }
548                            type = TOKEN_DIMENSION;
549                            return -1;
550                    }
551                case 'd':
552                case 'D':
553                    switch(nextChar())
554                    {
555                        case 'e':
556                        case 'E':
557                            switch (nextChar())
558                            {
559                                case 'g':
560                                case 'G':
561                                    nextChar();
562                                    if (current != -1 && isCSSNameCharacter(current))
563                                    {
564                                        do
565                                        {
566                                            nextChar();
567                                        } while (current != -1 && isCSSNameCharacter(current));
568                                        type = TOKEN_DIMENSION;
569                                        return -1;
570                                    }
571                                    type = TOKEN_DEG;
572                                    return -1;
573                            }
574                            default:
575                                while (current != -1 && isCSSNameCharacter(current))
576                                {
577                                    nextChar();
578                                }
579                                type = TOKEN_DIMENSION;
580                                return -1;
581                    }
582                case 'e':
583                case 'E':
584                    switch(nextChar())
585                    {
586                        case 'm':
587                        case 'M':
588                            nextChar();
589                            if (current != -1 && isCSSNameCharacter(current))
590                            {
591                                do
592                                {
593                                    nextChar();
594                                } while (current != -1 && isCSSNameCharacter(current));
595                                type = TOKEN_DIMENSION;
596                                return -1;
597                            }
598                            type = TOKEN_EM;
599                            return -1;
600                        case 'x':
601                        case 'X':
602                            nextChar();
603                            if (current != -1 && isCSSNameCharacter(current))
604                            {
605                                do
606                                {
607                                    nextChar();
608                                } while (current != -1 && isCSSNameCharacter(current));
609                                type = TOKEN_DIMENSION;
610                                return -1;
611                            }
612                            type = TOKEN_EX;
613                            return -1;
614                        default:
615                            while (current != -1 && isCSSNameCharacter(current))
616                            {
617                                nextChar();
618                            }
619                            type = TOKEN_DIMENSION;
620                            return -1;
621                    }
622                case 'g':
623                case 'G':
624                    switch(nextChar())
625                    {
626                        case 'r':
627                        case 'R':
628                            switch(nextChar())
629                            {
630                                case 'a':
631                                case 'A':
632                                    switch(nextChar())
633                                    {
634                                        case 'd':
635                                        case 'D':
636                                            nextChar();
637                                            if (current != -1 && isCSSNameCharacter(current))
638                                            {
639                                                do
640                                                {
641                                                    nextChar();
642                                                } while (current != -1 && isCSSNameCharacter(current));
643                                                type = TOKEN_DIMENSION;
644                                                return -1;
645                                            }
646                                            type = TOKEN_GRAD;
647                                            return -1;
648                                    }
649                            }
650                        default:
651                            while (current != -1 && isCSSNameCharacter(current))
652                            {
653                                nextChar();
654                            }
655                            type = TOKEN_DIMENSION;
656                            return -1;
657                    }
658                case 'h':
659                case 'H':
660                    nextChar();
661                    switch(current)
662                    {
663                        case 'z':
664                        case 'Z':
665                            nextChar();
666                            if (current != -1 && isCSSNameCharacter(current))
667                            {
668                                do
669                                {
670                                    nextChar();
671                                } while (current != -1 && isCSSNameCharacter(current));
672                                type = TOKEN_DIMENSION;
673                                return -1;
674                            }
675                            type = TOKEN_HZ;
676                            return -1;
677                        default:
678                            while (current != -1 && isCSSNameCharacter(current))
679                            {
680                                nextChar();
681                            }
682                            type = TOKEN_DIMENSION;
683                            return -1;
684                    }
685                case 'i':
686                case 'I':
687                    switch(nextChar())
688                    {
689                        case 'n':
690                        case 'N':
691                            nextChar();
692                            if (current != -1 && isCSSNameCharacter(current))
693                            {
694                                do
695                                {
696                                    nextChar();
697                                } while (current != -1 && isCSSNameCharacter(current));
698                                type = TOKEN_DIMENSION;
699                                return -1;
700                            }
701                            type = TOKEN_IN;
702                            return -1;
703                        default:
704                            while (current != -1 && isCSSNameCharacter(current))
705                            {
706                                nextChar();
707                            }
708                            type = TOKEN_DIMENSION;
709                            return -1;
710                    }
711                case 'k':
712                case 'K':
713                    switch(nextChar())
714                    {
715                        case 'h':
716                        case 'H':
717                            switch(nextChar())
718                            {
719                                case 'z':
720                                case 'Z':
721                                    nextChar();
722                                    if (current != -1 && isCSSNameCharacter(current))
723                                    {
724                                        do
725                                        {
726                                            nextChar();
727                                        } while (current != -1 && isCSSNameCharacter(current));
728                                        return TOKEN_DIMENSION;
729                                    }
730                                    return TOKEN_KHZ;
731                            }
732                        default:
733                            while (current != -1 && isCSSNameCharacter(current))
734                            {
735                                nextChar();
736                            }
737                            return TOKEN_DIMENSION;
738                    }
739                case 'm':
740                case 'M':
741                    switch(nextChar())
742                    {
743                        case 'm':
744                        case 'M':
745                            nextChar();
746                            if (current != -1 && isCSSNameCharacter(current))
747                            {
748                                do
749                                {
750                                    nextChar();
751                                } while (current != -1 && isCSSNameCharacter(current));
752                                return TOKEN_DIMENSION;
753                            }
754                            return TOKEN_MM;
755                        case 's':
756                        case 'S':
757                            nextChar();
758                            if (current != -1 && isCSSNameCharacter(current))
759                            {
760                                do
761                                {
762                                    nextChar();
763                                } while (current != -1 && isCSSNameCharacter(current));
764                                return TOKEN_DIMENSION;
765                            }
766                            return TOKEN_MS;
767                        default:
768                            while (current != -1 && isCSSNameCharacter(current))
769                            {
770                                nextChar();
771                            }
772                            return TOKEN_DIMENSION;
773                    }
774                    case 'p':
775                    case 'P':
776                        switch(nextChar())
777                        {
778                            case 'c':
779                            case 'C':
780                                nextChar();
781                                if (current != -1 && isCSSNameCharacter(current))
782                                {
783                                    do
784                                    {
785                                        nextChar();
786                                    } while (current != -1 && isCSSNameCharacter(current));
787                                    return TOKEN_DIMENSION;
788                                }
789                                return TOKEN_PC;
790                            case 't':
791                            case 'T':
792                                nextChar();
793                                if (current != -1 && isCSSNameCharacter(current))
794                                {
795                                    do
796                                    {
797                                        nextChar();
798                                    } while (current != -1 && isCSSNameCharacter(current));
799                                    return TOKEN_DIMENSION;
800                                }
801                                return TOKEN_PT;
802                            case 'x':
803                            case 'X':
804                                nextChar();
805                                if (current != -1 && isCSSNameCharacter(current))
806                                {
807                                    do
808                                    {
809                                        nextChar();
810                                    } while (current != -1 && isCSSNameCharacter(current));
811                                    return TOKEN_DIMENSION;
812                                }
813                                return TOKEN_PX;
814                            default:
815                                while (current != -1 && isCSSNameCharacter(current))
816                                {
817                                    nextChar();
818                                }
819                                return TOKEN_DIMENSION;
820                        }
821                    case 'r':
822                    case 'R':
823                        switch(nextChar())
824                        {
825                            case 'a':
826                            case 'A':
827                                switch(nextChar())
828                                {
829                                    case 'd':
830                                    case 'D':
831                                        nextChar();
832                                        if (current != -1 && isCSSNameCharacter(current))
833                                        {
834                                            do
835                                            {
836                                                nextChar();
837                                            } while (current != -1 && isCSSNameCharacter(current));
838                                            return TOKEN_DIMENSION;
839                                        }
840                                        return TOKEN_RAD;
841                                }
842                            default:
843                                while (current != -1 && isCSSNameCharacter(current))
844                                {
845                                    nextChar();
846                                }
847                                return TOKEN_DIMENSION;
848                        }
849                    case 's':
850                    case 'S':
851                        nextChar();
852                        return TOKEN_S;
853                    default:
854                        if (current != -1 && isCSSIdentifierStartCharacter(current))
855                        {
856                            do
857                            {
858                                nextChar();
859                            } while (current != -1 && isCSSNameCharacter(current));
860                            return TOKEN_DIMENSION;
861                        }
862                        return (integer) ? TOKEN_INTEGER : TOKEN_REAL;
863            }
864        }
865
866        /* Compares the given int with the given character, ignoring case. */
867        static bool isEqualIgnoreCase(int i, char c)
868        {
869            return (i == -1) ? false : tolower(i) == c;
870        }
871
872    public:
873        /* gets the next token. Return -1 on success, 0 on error */
874        int nextToken()
875        {
876            switch (current)
877            {
878                case -1:
879                    type = TOKEN_EOF;
880                    return -1;
881                case '{':
882                    nextChar();
883                    type = TOKEN_LEFT_CURLY_BRACE;
884                    return -1;
885                case '}':
886                    nextChar();
887                    type = TOKEN_RIGHT_CURLY_BRACE;
888                    return -1;
889                case '=':
890                    nextChar();
891                    type = TOKEN_EQUAL;
892                    return -1;
893                case '+':
894                    nextChar();
895                    type = TOKEN_PLUS;
896                    return -1;
897                case ',':
898                    nextChar();
899                    type = TOKEN_COMMA;
900                    return -1;
901                case ';':
902                    nextChar();
903                    type = TOKEN_SEMI_COLON;
904                    return -1;
905                case '>':
906                    nextChar();
907                    type = TOKEN_PRECEDE;
908                    return -1;
909                case '[':
910                    nextChar();
911                    type = TOKEN_LEFT_BRACKET;
912                    return -1;
913                case ']':
914                    nextChar();
915                    type = TOKEN_RIGHT_BRACKET;
916                    return -1;
917                case '*':
918                    nextChar();
919                    type = TOKEN_ANY;
920                    return -1;
921                case '(':
922                    nextChar();
923                    type = TOKEN_LEFT_BRACE;
924                    return -1;
925                case ')':
926                    nextChar();
927                    type = TOKEN_RIGHT_BRACE;
928                    return -1;
929                case ':':
930                    nextChar();
931                    type = TOKEN_COLON;
932                    return -1;
933                case ' ':
934                case '\t':
935                case '\r':
936                case '\n':
937                case '\f':
938                    do
939                    {
940                        nextChar();
941                    } while (isCSSSpace(current));
942                    type = TOKEN_SPACE;
943                    return -1;
944                case '/':
945                    nextChar();
946                    if (current != '*')
947                    {
948                        type = TOKEN_DIVIDE;
949                        return -1;
950                    }
951                    // Comment
952                    nextChar();
953                    start = position - 1;
954                    do
955                    {
956                        while (current != -1 && current != '*')
957                        {
958                            nextChar();
959                        }
960                        do
961                        {
962                            nextChar();
963                        } while (current != -1 && current == '*');
964                    } while (current != -1 && current != '/');
965                    if (current == -1)
966                    {
967                        PyErr_Format(PyExc_ValueError,"eof while looking for end of comment at line %d column %d", getLine(), getColumn());
968                        return 0;
969                    }
970                    nextChar();
971                    type = TOKEN_COMMENT;
972                    return -1;
973                case '\'': // String1
974                    return string1();
975                case '"': // String2
976                    return string2();
977                case '<':
978                    nextChar();
979                    if (current != '!')
980                    {
981                        PyErr_Format(PyExc_ValueError,"wrong char while looking for ! at line %d column %d", getLine(), getColumn());
982                        return 0;
983                    }
984                    nextChar();
985                    if (current == '-')
986                    {
987                        nextChar();
988                        if (current == '-')
989                        {
990                            nextChar();
991                            type = TOKEN_CDO;
992                            return -1;
993                        }
994                    }
995                    PyErr_Format(PyExc_ValueError,"wrong char while looking for ! at line %d column %d", getLine(), getColumn());
996                    return 0;
997                case '-':
998                    nextChar();
999                    if (current != '-')
1000                    {
1001                        type = TOKEN_MINUS;
1002                        return -1;
1003                    }
1004                    nextChar();
1005                    if (current == '>')
1006                    {
1007                        nextChar();
1008                        type = TOKEN_CDC;
1009                        return -1;
1010                    }
1011                    PyErr_Format(PyExc_ValueError,"wrong char while looking for - or > at line %d column %d", getLine(), getColumn());
1012                    return 0;
1013                case '|':
1014                    nextChar();
1015                    if (current == '=')
1016                    {
1017                        nextChar();
1018                        type = TOKEN_DASHMATCH;
1019                        return -1;
1020                    }
1021                    PyErr_Format(PyExc_ValueError,"wrong char while looking for = in |= at line %d column %d", getLine(), getColumn());
1022                    return 0;
1023                case '~':
1024                    nextChar();
1025                    if (current == '=')
1026                    {
1027                        nextChar();
1028                        type = TOKEN_INCLUDES;
1029                        return -1;
1030                    }
1031                    PyErr_Format(PyExc_ValueError,"wrong char while looking for = in ~= at line %d column %d", getLine(), getColumn());
1032                    return 0;
1033                case '#':
1034                    nextChar();
1035                    if (isCSSNameCharacter(current))
1036                    {
1037                        start = position - 1;
1038                        do
1039                        {
1040                            nextChar();
1041                            if (current == '\\')
1042                            {
1043                                nextChar();
1044                                escape();
1045                            }
1046                        } while (current != -1 && isCSSNameCharacter(current));
1047                        type = TOKEN_HASH;
1048                        return -1;
1049                    }
1050                    PyErr_Format(PyExc_ValueError,"wrong char in # at line %d column %d", getLine(), getColumn());
1051                    return 0;
1052                case '@':
1053                    nextChar();
1054                    switch (current)
1055                    {
1056                        case 'c':
1057                        case 'C':
1058                            start = position - 1;
1059                            if (isEqualIgnoreCase(nextChar(), 'h') &&
1060                                isEqualIgnoreCase(nextChar(), 'a') &&
1061                                isEqualIgnoreCase(nextChar(), 'r') &&
1062                                isEqualIgnoreCase(nextChar(), 's') &&
1063                                isEqualIgnoreCase(nextChar(), 'e') &&
1064                                isEqualIgnoreCase(nextChar(), 't'))
1065                            {
1066                                nextChar();
1067                                type = TOKEN_CHARSET_SYMBOL;
1068                                return -1;
1069                            }
1070                            break;
1071                        case 'f':
1072                        case 'F':
1073                            start = position - 1;
1074                            if (isEqualIgnoreCase(nextChar(), 'o') &&
1075                                isEqualIgnoreCase(nextChar(), 'n') &&
1076                                isEqualIgnoreCase(nextChar(), 't') &&
1077                                isEqualIgnoreCase(nextChar(), '-') &&
1078                                isEqualIgnoreCase(nextChar(), 'f') &&
1079                                isEqualIgnoreCase(nextChar(), 'a') &&
1080                                isEqualIgnoreCase(nextChar(), 'c') &&
1081                                isEqualIgnoreCase(nextChar(), 'e'))
1082                            {
1083                                nextChar();
1084                                type = TOKEN_FONT_FACE_SYMBOL;
1085                                return -1;
1086                            }
1087                            break;
1088                        case 'i':
1089                        case 'I':
1090                            start = position - 1;
1091                            if (isEqualIgnoreCase(nextChar(), 'm') &&
1092                                isEqualIgnoreCase(nextChar(), 'p') &&
1093                                isEqualIgnoreCase(nextChar(), 'o') &&
1094                                isEqualIgnoreCase(nextChar(), 'r') &&
1095                                isEqualIgnoreCase(nextChar(), 't'))
1096                            {
1097                                nextChar();
1098                                type = TOKEN_IMPORT_SYMBOL;
1099                                return -1;
1100                            }
1101                            break;
1102                        case 'm':
1103                        case 'M':
1104                            start = position - 1;
1105                            if (isEqualIgnoreCase(nextChar(), 'e') &&
1106                                isEqualIgnoreCase(nextChar(), 'd') &&
1107                                isEqualIgnoreCase(nextChar(), 'i') &&
1108                                isEqualIgnoreCase(nextChar(), 'a'))
1109                            {
1110                                nextChar();
1111                                type = TOKEN_MEDIA_SYMBOL;
1112                                return -1;
1113                            }
1114                            break;
1115                        case 'p':
1116                        case 'P':
1117                            start = position - 1;
1118                            if (isEqualIgnoreCase(nextChar(), 'a') &&
1119                                isEqualIgnoreCase(nextChar(), 'g') &&
1120                                isEqualIgnoreCase(nextChar(), 'e'))
1121                            {
1122                                nextChar();
1123                                type = TOKEN_PAGE_SYMBOL;
1124                                return -1;
1125                            }
1126                            break;
1127                        default:
1128                            if (!isCSSIdentifierStartCharacter(current))
1129                            {
1130                                PyErr_Format(PyExc_ValueError,"wrong char at line %d column %d", getLine(), getColumn());
1131                                return 0;
1132                            }
1133                            start = position - 1;
1134                    }
1135                    do
1136                    {
1137                        nextChar();
1138                        if (current == '\\')
1139                        {
1140                            nextChar();
1141                            escape();
1142                        }
1143                    } while (current != -1 && isCSSNameCharacter(current));
1144                    type = TOKEN_AT_KEYWORD;
1145                    return -1;
1146                case '!':
1147                    do
1148                    {
1149                        nextChar();
1150                    } while (current != -1 && isCSSSpace(current));
1151                    if (isEqualIgnoreCase(current, 'i') &&
1152                        isEqualIgnoreCase(nextChar(), 'm') &&
1153                        isEqualIgnoreCase(nextChar(), 'p') &&
1154                        isEqualIgnoreCase(nextChar(), 'o') &&
1155                        isEqualIgnoreCase(nextChar(), 'r') &&
1156                        isEqualIgnoreCase(nextChar(), 't') &&
1157                        isEqualIgnoreCase(nextChar(), 'a') &&
1158                        isEqualIgnoreCase(nextChar(), 'n') &&
1159                        isEqualIgnoreCase(nextChar(), 't'))
1160                    {
1161                        nextChar();
1162                        type = TOKEN_IMPORTANT_SYMBOL;
1163                        return -1;
1164                    }
1165                    if (current == -1)
1166                        PyErr_Format(PyExc_ValueError,"eof when parsing !important at line %d column %d", getLine(), getColumn());
1167                    else
1168                        PyErr_Format(PyExc_ValueError,"wrong char when parsing !important at line %d column %d", getLine(), getColumn());
1169                    return 0;
1170                case '0': case '1': case '2': case '3': case '4':
1171                case '5': case '6': case '7': case '8': case '9':
1172                    return number();
1173                case '.':
1174                    switch (nextChar())
1175                    {
1176                        case '0': case '1': case '2': case '3': case '4':
1177                        case '5': case '6': case '7': case '8': case '9':
1178                            return dotNumber();
1179                        default:
1180                            type = TOKEN_DOT;
1181                            return -1;
1182                    }
1183                case 'u':
1184                case 'U':
1185                    nextChar();
1186                    switch (current)
1187                    {
1188                        case '+':
1189                        {
1190                            bool range = false;
1191                            for (int i = 0; i < 6; i++)
1192                            {
1193                                nextChar();
1194                                switch (current)
1195                                {
1196                                    case '?':
1197                                        range = true;
1198                                        break;
1199                                    default:
1200                                        if (range && !isCSSHexadecimalCharacter(current))
1201                                        {
1202                                            PyErr_Format(PyExc_ValueError,"unicode at line %d column %d", getLine(), getColumn());
1203                                            return 0;
1204                                        }
1205                                }
1206                            }
1207                            nextChar();
1208                            if (range)
1209                            {
1210                                type = TOKEN_UNICODE_RANGE;
1211                                return -1;
1212                            }
1213                            if (current == '-')
1214                            {
1215                                nextChar();
1216                                if (!isCSSHexadecimalCharacter(current))
1217                                {
1218                                    PyErr_Format(PyExc_ValueError,"unicode at line %d column %d", getLine(), getColumn());
1219                                    return 0;
1220                                }
1221                                nextChar();
1222                                if (!isCSSHexadecimalCharacter(current))
1223                                {
1224                                    type = TOKEN_UNICODE_RANGE;
1225                                    return -1;
1226                                }
1227                                nextChar();
1228                                if (!isCSSHexadecimalCharacter(current))
1229                                {
1230                                    type = TOKEN_UNICODE_RANGE;
1231                                    return -1;
1232                                }
1233                                nextChar();
1234                                if (!isCSSHexadecimalCharacter(current))
1235                                {
1236                                    type = TOKEN_UNICODE_RANGE;
1237                                    return -1;
1238                                }
1239                                nextChar();
1240                                if (!isCSSHexadecimalCharacter(current))
1241                                {
1242                                    type = TOKEN_UNICODE_RANGE;
1243                                    return -1;
1244                                }
1245                                nextChar();
1246                                if (!isCSSHexadecimalCharacter(current))
1247                                {
1248                                    type = TOKEN_UNICODE_RANGE;
1249                                    return -1;
1250                                }
1251                                nextChar();
1252                                type = TOKEN_UNICODE_RANGE;
1253                                return -1;
1254                            }
1255                        }
1256                        case 'r':
1257                        case 'R':
1258                            nextChar();
1259                            switch (current)
1260                            {
1261                                case 'l':
1262                                case 'L':
1263                                    nextChar();
1264                                    switch (current)
1265                                    {
1266                                        case '(':
1267                                            do
1268                                            {
1269                                                nextChar();
1270                                            } while (current != -1 && isCSSSpace(current));
1271                                            switch (current)
1272                                            {
1273                                                case '\'':
1274                                                    string1();
1275                                                    blankCharacters += 2;
1276                                                    while (current != -1 && isCSSSpace(current))
1277                                                    {
1278                                                        blankCharacters++;
1279                                                        nextChar();
1280                                                    }
1281                                                    if (current == -1)
1282                                                    {
1283                                                        PyErr_Format(PyExc_ValueError,"eof while parsing URL at line %d column %d", getLine(), getColumn());
1284                                                        return 0;
1285                                                    }
1286                                                    if (current != ')')
1287                                                    {
1288                                                        PyErr_Format(PyExc_ValueError,"wrong char while parsing URL at line %d column %d", getLine(), getColumn());
1289                                                        return 0;
1290                                                    }
1291                                                    nextChar();
1292                                                    type = TOKEN_URI;
1293                                                    return -1;
1294                                                case '"':
1295                                                    string2();
1296                                                    blankCharacters += 2;
1297                                                    while (current != -1 && isCSSSpace(current))
1298                                                    {
1299                                                        blankCharacters++;
1300                                                        nextChar();
1301                                                    }
1302                                                    if (current == -1)
1303                                                    {
1304                                                        PyErr_Format(PyExc_ValueError,"eof while parsing URL at line %d column %d", getLine(), getColumn());
1305                                                        return 0;
1306                                                    }
1307                                                    if (current != ')')
1308                                                    {
1309                                                        PyErr_Format(PyExc_ValueError,"wrong char while parsing URL at line %d column %d", getLine(), getColumn());
1310                                                        return 0;
1311                                                    }
1312                                                    nextChar();
1313                                                    type = TOKEN_URI;
1314                                                    return -1;
1315                                                case ')':
1316                                                    PyErr_Format(PyExc_ValueError,"wrong char while parsing URL at line %d column %d", getLine(), getColumn());
1317                                                    return 0;
1318                                                default:
1319                                                    if (!isCSSURICharacter(current))
1320                                                    {
1321                                                        PyErr_Format(PyExc_ValueError,"wrong char in URL at line %d column %d", getLine(), getColumn());
1322                                                        return 0;
1323                                                    }
1324                                                    start = position - 1;
1325                                                    do
1326                                                    {
1327                                                        nextChar();
1328                                                    } while (current != -1 && isCSSURICharacter(current));
1329                                                    blankCharacters++;
1330                                                    while (current != -1 && isCSSSpace(current))
1331                                                    {
1332                                                        blankCharacters++;
1333                                                        nextChar();
1334                                                    }
1335                                                    if (current == -1)
1336                                                    {
1337                                                        PyErr_Format(PyExc_ValueError,"eof while parsing URL at line %d column %d", getLine(), getColumn());
1338                                                        return 0;
1339                                                    }
1340                                                    if (current != ')')
1341                                                    {
1342                                                        PyErr_Format(PyExc_ValueError,"wrong char while parsing URL at line %d column %d", getLine(), getColumn());
1343                                                        return 0;
1344                                                    }
1345                                                    nextChar();
1346                                                    type = TOKEN_URI;
1347                                                    return -1;
1348                                            }
1349                                    }
1350                            }
1351                    }
1352                    while (current != -1 && isCSSNameCharacter(current))
1353                    {
1354                        nextChar();
1355                    }
1356                    if (current == '(')
1357                    {
1358                        nextChar();
1359                        type = TOKEN_FUNCTION;
1360                        return -1;
1361                    }
1362                    type = TOKEN_IDENTIFIER;
1363                    return -1;
1364                default:
1365                    if (isCSSIdentifierStartCharacter(current))
1366                    {
1367                        // Identifier
1368                        do
1369                        {
1370                            nextChar();
1371                            if (current == '\\')
1372                            {
1373                                nextChar();
1374                                escape();
1375                            }
1376                        } while (current != -1 && isCSSNameCharacter(current));
1377                        if (current == '(')
1378                        {
1379                            nextChar();
1380                            type = TOKEN_FUNCTION;
1381                            return -1;
1382                        }
1383                        type = TOKEN_IDENTIFIER;
1384                        return -1;
1385                    }
1386                    nextChar();
1387                    PyErr_Format(PyExc_ValueError,"illegal char at line %d column %d", getLine(), getColumn());
1388                    return 0;
1389            }
1390            return -1;
1391        }
1392};
1393
1394static int callWithVoid(CSSTokenizer *self, PyObject *callback)
1395{
1396    if (callback)
1397    {
1398        PyObject *res = PyObject_CallFunction(callback, NULL);
1399        if (!res)
1400            return 0;
1401        Py_DECREF(res);
1402    }
1403    return -1;
1404}
1405
1406/*
1407static int callWithString(CSSTokenizer *self, PyObject *callback, const char *data, int len)
1408{
1409    if (callback)
1410    {
1411        PyObject *res = PyObject_CallFunction(callback, "s#", data, len);
1412        if (!res)
1413            return 0;
1414        Py_DECREF(res);
1415    }
1416    return -1;
1417}
1418*/
1419
1420static int callWith2Strings(CSSTokenizer *self, PyObject *callback, const char *data1, int len1, const char *data2, int len2)
1421{
1422    if (callback)
1423    {
1424        PyObject *res = PyObject_CallFunction(callback, "s#s#", data1, len1, data2, len2);
1425        if (!res)
1426            return 0;
1427        Py_DECREF(res);
1428    }
1429    return -1;
1430}
1431
1432#define GETCB(member, name) Py_XDECREF(self->member); self->member = PyObject_GetAttrString(item, name);
1433
1434static PyObject *parser_register(CSSTokenizer *self, PyObject *args)
1435{
1436    /* register a callback object */
1437    PyObject* item;
1438    if (!PyArg_ParseTuple(args, "O", &item))
1439      return NULL;
1440
1441    GETCB(startDocument, "startDocument");
1442    GETCB(endDocument, "endDocument");
1443    GETCB(token, "token");
1444
1445    PyErr_Clear();
1446
1447    Py_INCREF(Py_None);
1448    return Py_None;
1449}
1450
1451static PyObject *parser_parse(CSSTokenizer *self, PyObject *args)
1452{
1453    const char *s;
1454    int len;
1455
1456    if (!PyArg_ParseTuple(args, "s#:parse", &s, &len))
1457        return NULL;
1458
1459    if (!callWithVoid(self, self->startDocument))
1460        return NULL;
1461
1462    Scanner scanner(s, len);
1463
1464    Token t;
1465    if (!scanner.next())
1466        return NULL;
1467    while ((t = scanner.getType()) != TOKEN_EOF)
1468    {
1469        if (!callWith2Strings(self, self->token,
1470                    token_names[t], strlen(token_names[t]),
1471                scanner.buffer+scanner.start, scanner.end-scanner.start))
1472            return NULL;
1473        if (!scanner.next())
1474            return NULL;
1475    }
1476    if (!callWithVoid(self, self->endDocument))
1477        return NULL;
1478
1479    Py_INCREF(Py_None);
1480    return Py_None;
1481}
1482
1483static PyMethodDef CSSTokenizer_methods[] =
1484{
1485    /* register callbacks */
1486    {"register", (PyCFunction) parser_register, METH_VARARGS},
1487    /* one-shot parsing */
1488    {"parse", (PyCFunction) parser_parse, METH_VARARGS},
1489    {NULL, NULL}
1490};
1491
1492static char parser_new__doc__[] = "create a parser";
1493
1494static PyObject *parser_new(PyObject *noself, PyObject *args)
1495{
1496    if (!PyArg_NoArgs(args))
1497        return NULL;
1498
1499    CSSTokenizer *self = PyObject_NEW(CSSTokenizer, &CSSTokenizer_Type);
1500    self->startDocument = NULL;
1501    self->endDocument = NULL;
1502    self->token = NULL;
1503    return (PyObject *)self;
1504}
1505
1506static void parser_del(CSSTokenizer *self)
1507{
1508    Py_XDECREF(self->startDocument);
1509    Py_XDECREF(self->endDocument);
1510    Py_XDECREF(self->token);
1511    PyObject_DEL(self);
1512}
1513
1514static PyObject *parser_getattr(CSSTokenizer *self, char *name)
1515{
1516    return Py_FindMethod(CSSTokenizer_methods, (PyObject*) self, name);
1517}
1518
1519PyTypeObject CSSTokenizer_Type =
1520{
1521    PyObject_HEAD_INIT(NULL)
1522    0, /* ob_size */
1523    "CSSTokenizer", /* tp_name */
1524    sizeof(CSSTokenizer), /* tp_size */
1525    0, /* tp_itemsize */
1526    /* methods */
1527    (destructor)parser_del, /* tp_dealloc */
1528    0, /* tp_print */
1529    (getattrfunc)parser_getattr, /* tp_getattr */
1530    0 /* tp_setattr */
1531};
1532
1533static PyMethodDef _functions[] =
1534{
1535    {"CSSTokenizer", parser_new, METH_NOARGS, parser_new__doc__},
1536    {NULL, NULL}
1537};
1538
1539static char module__doc__[] =
1540"a CSS parser.";
1541
1542extern "C" void
1543#ifdef WIN32
1544__declspec(dllexport)
1545#endif
1546initcsstokenizer(void)
1547{
1548    Py_InitModule3("csstokenizer", _functions, module__doc__);
1549}
1550
1551/*
1552 ============================================================================
1553                   The Apache Software License, Version 1.1
1554 ============================================================================
1555
1556 Copyright (C) 2000 The Apache Software Foundation. All rights reserved.
1557
1558 Redistribution and use in source and binary forms, with or without modifica-
1559 tion, are permitted provided that the following conditions are met:
1560
1561 1. Redistributions of  source code must  retain the above copyright  notice,
1562    this list of conditions and the following disclaimer.
1563
1564 2. Redistributions in binary form must reproduce the above copyright notice,
1565    this list of conditions and the following disclaimer in the documentation
1566    and/or other materials provided with the distribution.
1567
1568 3. The end-user documentation included with the redistribution, if any, must
1569    include  the following  acknowledgment:  "This product includes  software
1570    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
1571    Alternately, this  acknowledgment may  appear in the software itself,  if
1572    and wherever such third-party acknowledgments normally appear.
1573
1574 4. The names "Batik" and  "Apache Software Foundation"  must not be  used to
1575    endorse  or promote  products derived  from this  software without  prior
1576    written permission. For written permission, please contact
1577    apache@apache.org.
1578
1579 5. Products  derived from this software may not  be called "Apache", nor may
1580    "Apache" appear  in their name,  without prior written permission  of the
1581    Apache Software Foundation.
1582
1583 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
1584 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1585 FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
1586 APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
1587 INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
1588 DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
1589 OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
1590 ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
1591 (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
1592 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1593
1594 This software  consists of voluntary contributions made  by many individuals
1595 on  behalf  of the Apache Software  Foundation. For more  information on the
1596 Apache Software Foundation, please see <http://www.apache.org/>.
1597*/
Note: See TracBrowser for help on using the browser.