xref: /AOO41X/main/sw/source/filter/html/parcss1.cxx (revision efeef26f81c84063fb0a91bde3856d4a51172d90)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <limits.h>
32 #include <rtl/ustrbuf.hxx>
33 #include <tools/debug.hxx>
34 #include <vcl/svapp.hxx>
35 #include <svtools/htmltokn.h>
36 
37 #include "css1kywd.hxx"
38 #include "parcss1.hxx"
39 
40 
41 // Loop-Check: Um Endlos-Schleifen zu vermeiden, wird in jeder
42 // Schalife geprueft, ob ein Fortschritt in der Eingabe-Position
43 // stattgefunden hat
44 #define LOOP_CHECK
45 
46 #ifdef LOOP_CHECK
47 
48 #define LOOP_CHECK_DECL \
49     xub_StrLen nOldInPos = STRING_MAXLEN;
50 #define LOOP_CHECK_RESTART \
51     nOldInPos = STRING_MAXLEN;
52 #define LOOP_CHECK_CHECK( where ) \
53     DBG_ASSERT( nOldInPos!=nInPos || cNextCh==(sal_Unicode)EOF, where );    \
54     if( nOldInPos==nInPos && cNextCh!=(sal_Unicode)EOF )                    \
55         break;                                                              \
56     else                                                                    \
57         nOldInPos = nInPos;
58 
59 #else
60 
61 #define LOOP_CHECK_DECL
62 #define LOOP_CHECK_RESTART
63 #define LOOP_CHECK_CHECK( where )
64 
65 #endif
66 
67 
68 
69 const sal_Int32 MAX_LEN = 1024;
70 
71 /*  */
72 
InitRead(const String & rIn)73 void CSS1Parser::InitRead( const String& rIn )
74 {
75     nlLineNr = 0;
76     nlLinePos = 0;
77 
78     bWhiteSpace = sal_True; // Wenn noch nichts gelesen wurde ist das wie WS
79     bEOF = sal_False;
80     eState = CSS1_PAR_WORKING;
81     nValue = 0.;
82 
83     aIn = rIn;
84     nInPos = 0;
85     cNextCh = GetNextChar();
86     nToken = GetNextToken();
87 }
88 
GetNextChar()89 sal_Unicode CSS1Parser::GetNextChar()
90 {
91     if( nInPos >= aIn.Len() )
92     {
93         bEOF = sal_True;
94         return (sal_Unicode)EOF;
95     }
96 
97     sal_Unicode c = aIn.GetChar( nInPos );
98     nInPos++;
99 
100     if( c == '\n' )
101     {
102         IncLineNr();
103         SetLinePos( 1L );
104     }
105     else
106         IncLinePos();
107 
108     return c;
109 }
110 
111 /*  */
112 
113 // Diese Funktion realisiert den in
114 //
115 //      http://www.w3.orh/pub/WWW/TR/WD-css1.html
116 // bzw. http://www.w3.orh/pub/WWW/TR/WD-css1-960220.html
117 //
118 // beschriebenen Scanner fuer CSS1. Es handelt sich um eine direkte
119 // Umsetzung der dort beschriebenen Lex-Grammatik
120 //
GetNextToken()121 CSS1Token CSS1Parser::GetNextToken()
122 {
123     CSS1Token nRet = CSS1_NULL;
124     aToken.Erase();
125 
126     do {
127         // Merken, ob davor White-Space gelesen wurde
128         sal_Bool bPrevWhiteSpace = bWhiteSpace;
129         bWhiteSpace = sal_False;
130 
131         sal_Bool bNextCh = sal_True;
132         switch( cNextCh )
133         {
134         case '/': // COMMENT | '/'
135             {
136                 cNextCh = GetNextChar();
137                 if( '*' == cNextCh )
138                 {
139                     // COMMENT
140                     cNextCh = GetNextChar();
141 
142                     sal_Bool bAsterix = sal_False;
143                     while( !(bAsterix && '/'==cNextCh) && !IsEOF() )
144                     {
145                         bAsterix = ('*'==cNextCh);
146                         cNextCh = GetNextChar();
147                     }
148                 }
149                 else
150                 {
151                     // '/'
152                     bNextCh = sal_False;
153                     nRet = CSS1_SLASH;
154                 }
155             }
156             break;
157 
158         case '@': // '@import' | '@XXX'
159             {
160                 cNextCh = GetNextChar();
161                 if( ('A' <= cNextCh && cNextCh <= 'Z') ||
162                     ('a' <= cNextCh && cNextCh <= 'z') )
163                 {
164                     // den naechsten Identifer scannen
165                     ::rtl::OUStringBuffer sTmpBuffer( 32L );
166                     do {
167                         sTmpBuffer.append( cNextCh );
168                         cNextCh = GetNextChar();
169                     } while( ('A' <= cNextCh && cNextCh <= 'Z') ||
170                              ('a' <= cNextCh && cNextCh <= 'z') ||
171                              ('0' <= cNextCh && cNextCh <= '9') ||
172                              '-'==cNextCh && !IsEOF() );
173 
174                     aToken += String(sTmpBuffer.makeStringAndClear());
175 
176                     // und schauen, ob wir ihn kennen
177                     switch( aToken.GetChar(0) )
178                     {
179                     case 'i':
180                     case 'I':
181                         if( aToken.EqualsIgnoreCaseAscii(sCSS1_import) )
182                             nRet = CSS1_IMPORT_SYM;
183                         break;
184 // /Feature: PrintExt
185                     case 'p':
186                     case 'P':
187                         if( aToken.EqualsIgnoreCaseAscii(sCSS1_page) )
188                             nRet = CSS1_PAGE_SYM;
189                         break;
190 // /Feature: PrintExt
191                     }
192 
193                     // Fehlerbehandlung: '@ident' und alles bis
194                     // zu einem Semikolon der dem Ende des folgenden
195                     // Blocks ignorieren
196                     if( CSS1_NULL==nRet )
197                     {
198                         aToken.Erase();
199                         sal_uInt16 nBlockLvl = 0;
200                         sal_Unicode cQuoteCh = 0;
201                         sal_Bool bDone = sal_False, bEscape = sal_False;
202                         while( !bDone && !IsEOF() )
203                         {
204                             sal_Bool bOldEscape = bEscape;
205                             bEscape = sal_False;
206                             switch( cNextCh )
207                             {
208                             case '{':
209                                 if( !cQuoteCh && !bOldEscape )
210                                     nBlockLvl++;;
211                                 break;
212                             case ';':
213                                 if( !cQuoteCh && !bOldEscape )
214                                     bDone = nBlockLvl==0;
215                                 break;
216                             case '}':
217                                 if( !cQuoteCh && !bOldEscape )
218                                     bDone = --nBlockLvl==0;
219                                 break;
220                             case '\"':
221                             case '\'':
222                                 if( !bOldEscape )
223                                 {
224                                     if( cQuoteCh )
225                                     {
226                                         if( cQuoteCh == cNextCh )
227                                             cQuoteCh = 0;
228                                     }
229                                     else
230                                     {
231                                         cQuoteCh = cNextCh;
232                                     }
233                                 }
234                                 break;
235                             case '\\':
236                                 if( !bOldEscape )
237                                     bEscape = sal_True;
238                                 break;
239                             }
240                             cNextCh = GetNextChar();
241                         }
242                     }
243 
244                     bNextCh = sal_False;
245                 }
246             }
247             break;
248 
249         case '!': // '!' 'legal' | '!' 'important' | syntax error
250             {
251                 // White Space ueberlesen
252                 cNextCh = GetNextChar();
253                 while( ( ' ' == cNextCh ||
254                        (cNextCh >= 0x09 && cNextCh <= 0x0d) ) && !IsEOF() )
255                 {
256                     bWhiteSpace = sal_True;
257                     cNextCh = GetNextChar();
258                 }
259 
260                 if( 'i'==cNextCh || 'I'==cNextCh)
261                 {
262                     // den naechsten Identifer scannen
263                     ::rtl::OUStringBuffer sTmpBuffer( 32L );
264                     do {
265                         sTmpBuffer.append( cNextCh );
266                         cNextCh = GetNextChar();
267                     } while( ('A' <= cNextCh && cNextCh <= 'Z') ||
268                              ('a' <= cNextCh && cNextCh <= 'z') ||
269                              ('0' <= cNextCh && cNextCh <= '9') ||
270                              '-' == cNextCh && !IsEOF() );
271 
272                     aToken += String(sTmpBuffer.makeStringAndClear());
273 
274                     if( ('i'==aToken.GetChar(0) || 'I'==aToken.GetChar(0)) &&
275                         aToken.EqualsIgnoreCaseAscii(sCSS1_important) )
276                     {
277                         // '!' 'important'
278                         nRet = CSS1_IMPORTANT_SYM;
279                     }
280                     else
281                     {
282                         // Fehlerbehandlung: '!' ignorieren, IDENT nicht
283                         nRet = CSS1_IDENT;
284                     }
285 
286                     bWhiteSpace = sal_False;
287                     bNextCh = sal_False;
288                 }
289                 else
290                 {
291                     // Fehlerbehandlung: '!' ignorieren
292                     bNextCh = sal_False;
293                 }
294             }
295             break;
296 
297         case '\"':
298         case '\'': // STRING
299             {
300                 // \... geht noch nicht!!!
301                 sal_Unicode cQuoteChar = cNextCh;
302                 cNextCh = GetNextChar();
303 
304                 ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
305                 do {
306                     sTmpBuffer.append( cNextCh );
307                     cNextCh = GetNextChar();
308                 } while( cQuoteChar != cNextCh && !IsEOF() );
309 
310                 aToken += String(sTmpBuffer.makeStringAndClear());
311 
312                 nRet = CSS1_STRING;
313             }
314             break;
315 
316         case '0':
317         case '1':
318         case '2':
319         case '3':
320         case '4':
321         case '5':
322         case '6':
323         case '7':
324         case '8':
325         case '9': // NUMBER | PERCENTAGE | LENGTH
326             {
327                 // die aktuelle Position retten
328                 xub_StrLen nInPosSave = nInPos;
329                 sal_Unicode cNextChSave = cNextCh;
330                 sal_uInt32 nlLineNrSave = nlLineNr;
331                 sal_uInt32 nlLinePosSave = nlLinePos;
332                 sal_Bool bEOFSave = bEOF;
333 
334                 // erstmal versuchen eine Hex-Zahl zu scannen
335                 ::rtl::OUStringBuffer sTmpBuffer( 16 );
336                 do {
337                     sTmpBuffer.append( cNextCh );
338                     cNextCh = GetNextChar();
339                 } while( sTmpBuffer.getLength() < 7 &&
340                          ( ('0'<=cNextCh && '9'>=cNextCh) ||
341                            ('A'<=cNextCh && 'F'>=cNextCh) ||
342                            ('a'<=cNextCh && 'f'>=cNextCh) ) &&
343                          !IsEOF() );
344 
345                 if( sTmpBuffer.getLength()==6 )
346                 {
347                     // wir haben eine hexadezimale Farbe gefunden
348                     aToken += String(sTmpBuffer.makeStringAndClear());
349                     nRet = CSS1_HEXCOLOR;
350                     bNextCh = sal_False;
351 
352                     break;
353                 }
354 
355                 // sonst versuchen wir es mit einer Zahl
356                 nInPos = nInPosSave;
357                 cNextCh = cNextChSave;
358                 nlLineNr = nlLineNrSave;
359                 nlLinePos = nlLinePosSave;
360                 bEOF = bEOFSave;
361 
362                 // erstmal die Zahl scannen
363                 sTmpBuffer.setLength( 0L );
364                 do {
365                     sTmpBuffer.append( cNextCh );
366                     cNextCh = GetNextChar();
367                 } while( (('0'<=cNextCh && '9'>=cNextCh) || '.'==cNextCh) &&
368                          !IsEOF() );
369 
370                 aToken += String(sTmpBuffer.makeStringAndClear());
371                 nValue = aToken.ToDouble();
372 
373                 // White Space ueberlesen
374                 while( ( ' ' == cNextCh ||
375                        (cNextCh >= 0x09 && cNextCh <= 0x0d) ) && !IsEOF() )
376                 {
377                     bWhiteSpace = sal_True;
378                     cNextCh = GetNextChar();
379                 }
380 
381                 // und nun Schauen, ob es eine Einheit gibt
382                 switch( cNextCh )
383                 {
384                 case '%': // PERCENTAGE
385                     bWhiteSpace = sal_False;
386                     nRet = CSS1_PERCENTAGE;
387                     break;
388 
389                 case 'c':
390                 case 'C': // LENGTH cm | LENGTH IDENT
391                 case 'e':
392                 case 'E': // LENGTH (em | ex) | LENGTH IDENT
393                 case 'i':
394                 case 'I': // LENGTH inch | LENGTH IDENT
395                 case 'p':
396                 case 'P': // LENGTH (pt | px | pc) | LENGTH IDENT
397                 case 'm':
398                 case 'M': // LENGTH mm | LENGTH IDENT
399                     {
400                         // die aktuelle Position retten
401                         xub_StrLen nInPosOld = nInPos;
402                         sal_Unicode cNextChOld = cNextCh;
403                         sal_uLong nlLineNrOld  = nlLineNr;
404                         sal_uLong nlLinePosOld = nlLinePos;
405                         sal_Bool bEOFOld = bEOF;
406 
407                         // den naechsten Identifer scannen
408                         String aIdent;
409                         ::rtl::OUStringBuffer sTmpBuffer2( 64L );
410                         do {
411                             sTmpBuffer2.append( cNextCh );
412                             cNextCh = GetNextChar();
413                         } while( ( ('A' <= cNextCh && cNextCh <= 'Z') ||
414                                    ('a' <= cNextCh && cNextCh <= 'z') ||
415                                    ('0' <= cNextCh && cNextCh <= '9') ||
416                                  '-'==cNextCh) && !IsEOF() );
417 
418                         aIdent += String(sTmpBuffer2.makeStringAndClear());
419 
420                         // Ist es eine Einheit?
421                         const sal_Char *pCmp1 = 0, *pCmp2 = 0, *pCmp3 = 0;
422                         double nScale1 = 1., nScale2 = 1., nScale3 = 1.;
423                         CSS1Token nToken1 = CSS1_LENGTH,
424                                   nToken2 = CSS1_LENGTH,
425                                   nToken3 = CSS1_LENGTH;
426                         switch( aIdent.GetChar(0) )
427                         {
428                         case 'c':
429                         case 'C':
430                             pCmp1 = sCSS1_UNIT_cm;
431                             nScale1 = (72.*20.)/2.54; // twip
432                             break;
433                         case 'e':
434                         case 'E':
435                             pCmp1 = sCSS1_UNIT_em;
436                             nToken1 = CSS1_EMS;
437 
438                             pCmp2 = sCSS1_UNIT_ex;
439                             nToken2 = CSS1_EMX;
440                             break;
441                         case 'i':
442                         case 'I':
443                             pCmp1 = sCSS1_UNIT_inch;
444                             nScale1 = 72.*20.; // twip
445                             break;
446                         case 'm':
447                         case 'M':
448                             pCmp1 = sCSS1_UNIT_mm;
449                             nScale1 = (72.*20.)/25.4; // twip
450                             break;
451                         case 'p':
452                         case 'P':
453                             pCmp1 = sCSS1_UNIT_pt;
454                             nScale1 = 20.; // twip
455 
456                             pCmp2 = sCSS1_UNIT_pc;
457                             nScale2 = 12.*20.; // twip
458 
459                             pCmp3 = sCSS1_UNIT_px;
460                             nToken3 = CSS1_PIXLENGTH;
461                             break;
462                         }
463 
464                         double nScale = 0.0;
465                         DBG_ASSERT( pCmp1, "Wo kommt das erste Zeichen her?" );
466                         if( aIdent.EqualsIgnoreCaseAscii(pCmp1) )
467                         {
468                             nScale = nScale1;
469                             nRet = nToken1;
470                         }
471                         else if( pCmp2 &&
472                                  aIdent.EqualsIgnoreCaseAscii(pCmp2) )
473                         {
474                             nScale = nScale2;
475                             nRet = nToken2;
476                         }
477                         else if( pCmp3 &&
478                                  aIdent.EqualsIgnoreCaseAscii(pCmp3) )
479                         {
480                             nScale = nScale3;
481                             nRet = nToken3;
482                         }
483                         else
484                         {
485                             nRet = CSS1_NUMBER;
486                         }
487 
488                         if( CSS1_LENGTH==nRet && nScale!=1.0 )
489                             nValue *= nScale;
490 
491                         if( nRet == CSS1_NUMBER )
492                         {
493                             nInPos = nInPosOld;
494                             cNextCh = cNextChOld;
495                             nlLineNr = nlLineNrOld;
496                             nlLinePos = nlLinePosOld;
497                             bEOF = bEOFOld;
498                         }
499                         else
500                         {
501                             bWhiteSpace = sal_False;
502                         }
503                         bNextCh = sal_False;
504                     }
505                     break;
506                 default: // NUMBER IDENT
507                     bNextCh = sal_False;
508                     nRet = CSS1_NUMBER;
509                     break;
510                 }
511             }
512             break;
513 
514         case ':': // ':'
515             // link/visited/active abfangen !!!
516             nRet = CSS1_COLON;
517             break;
518 
519         case '.': // DOT_W_WS | DOT_WO_WS
520             nRet = bPrevWhiteSpace ? CSS1_DOT_W_WS : CSS1_DOT_WO_WS;
521             break;
522 
523         // case '/': siehe oben
524 
525         case '+': // '+'
526             nRet = CSS1_PLUS;
527             break;
528 
529         case '-': // '-'
530             nRet = CSS1_MINUS;
531             break;
532 
533         case '{': // '{'
534             nRet = CSS1_OBRACE;
535             break;
536 
537         case '}': // '}'
538             nRet = CSS1_CBRACE;
539             break;
540 
541         case ';': // ';'
542             nRet = CSS1_SEMICOLON;
543             break;
544 
545         case ',': // ','
546             nRet = CSS1_COMMA;
547             break;
548 
549         case '#': // '#'
550             cNextCh = GetNextChar();
551             if( ('0'<=cNextCh && '9'>=cNextCh) ||
552                 ('a'<=cNextCh && 'f'>=cNextCh) ||
553                 ('A'<=cNextCh && 'F'>=cNextCh) )
554             {
555                 // die aktuelle Position retten
556                 xub_StrLen nInPosSave = nInPos;
557                 sal_Unicode cNextChSave = cNextCh;
558                 sal_uLong nlLineNrSave = nlLineNr;
559                 sal_uLong nlLinePosSave = nlLinePos;
560                 sal_Bool bEOFSave = bEOF;
561 
562                 // erstmal versuchen eine Hex-Zahl zu scannen
563                 ::rtl::OUStringBuffer sTmpBuffer( 6L );
564                 do {
565                     sTmpBuffer.append( cNextCh );
566                     cNextCh = GetNextChar();
567                 } while( sTmpBuffer.getLength() < 7 &&
568                          ( ('0'<=cNextCh && '9'>=cNextCh) ||
569                            ('A'<=cNextCh && 'F'>=cNextCh) ||
570                            ('a'<=cNextCh && 'f'>=cNextCh) ) &&
571                          !IsEOF() );
572 
573                 if( sTmpBuffer.getLength()==6 || sTmpBuffer.getLength()==3 )
574                 {
575                     // wir haben eine hexadezimale Farbe gefunden
576                     aToken += String(sTmpBuffer.makeStringAndClear());
577                     nRet = CSS1_HEXCOLOR;
578                     bNextCh = sal_False;
579 
580                     break;
581                 }
582 
583                 // sonst versuchen wir es mit einer Zahl
584                 nInPos = nInPosSave;
585                 cNextCh = cNextChSave;
586                 nlLineNr = nlLineNrSave;
587                 nlLinePos = nlLinePosSave;
588                 bEOF = bEOFSave;
589             }
590 
591             nRet = CSS1_HASH;
592             bNextCh = sal_False;
593             break;
594 
595         case ' ':
596         case '\t':
597         case '\r':
598         case '\n': // White-Space
599             bWhiteSpace = sal_True;
600             break;
601 
602         case (sal_Unicode)EOF:
603             if( IsEOF() )
604             {
605                 eState = CSS1_PAR_ACCEPTED;
606                 bNextCh = sal_False;
607                 break;
608             }
609             // kein break;
610 
611         default: // IDENT | syntax error
612             // TODO IsAlpha
613             if( ('A' <= cNextCh && cNextCh <= 'Z') ||
614                 ('a' <= cNextCh && cNextCh <= 'z') )
615             {
616                 // IDENT
617 
618                 sal_Bool bHexColor = sal_True;
619 
620                 // den naechsten Identifer scannen
621                 ::rtl::OUStringBuffer sTmpBuffer( 64L );
622                 do {
623                     sTmpBuffer.append( cNextCh );
624                     if( bHexColor )
625                     {
626                         bHexColor =  sTmpBuffer.getLength()<7 &&
627                                      ( ('0'<=cNextCh && '9'>=cNextCh) ||
628                                        ('A'<=cNextCh && 'F'>=cNextCh) ||
629                                        ('a'<=cNextCh && 'f'>=cNextCh) );
630                     }
631                     cNextCh = GetNextChar();
632                     // TODO: AlphaNumeric
633                 } while( ( ('0'<=cNextCh && '9'>=cNextCh) ||
634                            ('A'<=cNextCh && 'Z'>=cNextCh) ||
635                            ('a'<=cNextCh && 'z'>=cNextCh) ||
636                            '-'==cNextCh ) &&
637                          !IsEOF() );
638 
639                 aToken += String(sTmpBuffer.makeStringAndClear());
640 
641                 if( bHexColor && sTmpBuffer.getLength()==6 )
642                 {
643                     bNextCh = sal_False;
644                     nRet = CSS1_HEXCOLOR;
645 
646                     break;
647                 }
648                 if( '('==cNextCh &&
649                     ( (('u'==aToken.GetChar(0) || 'U'==aToken.GetChar(0)) &&
650                        aToken.EqualsIgnoreCaseAscii(sCSS1_url)) ||
651                       (('r'==aToken.GetChar(0) || 'R'==aToken.GetChar(0)) &&
652                        aToken.EqualsIgnoreCaseAscii(sCSS1_rgb)) ) )
653                 {
654                     sal_uInt16 nNestCnt = 0;
655                     ::rtl::OUStringBuffer sTmpBuffer2( 64L );
656                     do {
657                         sTmpBuffer2.append( cNextCh );
658                         switch( cNextCh )
659                         {
660                         case '(':   nNestCnt++; break;
661                         case ')':   nNestCnt--; break;
662                         }
663                         cNextCh = GetNextChar();
664                     } while( (nNestCnt>1 || ')'!=cNextCh) && !IsEOF() );
665                     sTmpBuffer2.append( cNextCh );
666                     aToken += String(sTmpBuffer2.makeStringAndClear());
667                     bNextCh = sal_True;
668                     nRet = 'u'==aToken.GetChar(0) || 'U'==aToken.GetChar(0)
669                                 ? CSS1_URL
670                                 : CSS1_RGB;
671                 }
672                 else
673                 {
674                     bNextCh = sal_False;
675                     nRet = CSS1_IDENT;
676                 }
677             }
678             // Fehlerbehandlung: Zeichen ignorieren
679             break;
680         }
681         if( bNextCh )
682             cNextCh = GetNextChar();
683 
684     } while( CSS1_NULL==nRet && IsParserWorking() );
685 
686     return nRet;
687 }
688 
689 
690 /*  */
691 
692 
693 // Dies folegenden Funktionen realisieren den in
694 //
695 //      http://www.w3.orh/pub/WWW/TR/WD-css1.html
696 // bzw. http://www.w3.orh/pub/WWW/TR/WD-css1-960220.html
697 //
698 // beschriebenen Parser fuer CSS1. Es handelt sich um eine direkte
699 // Umsetzung der dort beschriebenen Grammatik
700 
701 // stylesheet
702 //  : import* rule*
703 //
704 // import
705 //  : IMPORT_SYM url
706 //
707 // url
708 //  : STRING
709 //
ParseStyleSheet()710 void CSS1Parser::ParseStyleSheet()
711 {
712     LOOP_CHECK_DECL
713 
714     // import*
715     sal_Bool bDone = sal_False;
716     while( !bDone && IsParserWorking() )
717     {
718         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleSheet()/import *" )
719 
720         switch( nToken )
721         {
722         case CSS1_IMPORT_SYM:
723             // IMPORT_SYM url
724             // url ueberspringen wir ungeprueft
725             nToken = GetNextToken();
726             break;
727         case CSS1_IDENT:            // Look-Aheads
728         case CSS1_DOT_W_WS:
729         case CSS1_HASH:
730 // /Feature: PrintExt
731         case CSS1_PAGE_SYM:
732 // /Feature: PrintExt
733             // rule
734             bDone = sal_True;
735             break;
736         default:
737             // Fehlerbehandlung: ueberlesen
738             break;
739         }
740 
741         if( !bDone )
742             nToken = GetNextToken();
743     }
744 
745     LOOP_CHECK_RESTART
746 
747     // rule *
748     while( IsParserWorking() )
749     {
750         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleSheet()/rule *" )
751 
752         switch( nToken )
753         {
754         case CSS1_IDENT:        // Look-Aheads
755         case CSS1_DOT_W_WS:
756         case CSS1_HASH:
757 // /Feature: PrintExt
758         case CSS1_PAGE_SYM:
759 // /Feature: PrintExt
760             // rule
761             ParseRule();
762             break;
763         default:
764             // Fehlerbehandlung: ueberlesen
765             nToken = GetNextToken();
766             break;
767         }
768     }
769 }
770 
771 // rule
772 //  : selector [ ',' selector ]*
773 //    '{' declaration [ ';' declaration ]* '}'
774 //
ParseRule()775 void CSS1Parser::ParseRule()
776 {
777     // selector
778     CSS1Selector *pSelector = ParseSelector();
779     if( !pSelector )
780         return;
781 
782     // Selektor verarbeiten
783     if( SelectorParsed( pSelector, sal_True ) )
784         delete pSelector;
785 
786     LOOP_CHECK_DECL
787 
788     // [ ',' selector ]*
789     while( CSS1_COMMA==nToken && IsParserWorking() )
790     {
791         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseRule()/selector *" )
792 
793         // ',' ueberelesen
794         nToken = GetNextToken();
795 
796         // selector
797         pSelector = ParseSelector();
798         if( !pSelector )
799             return;
800 
801         // Selektor verarbeiten
802         if( SelectorParsed( pSelector, sal_False ) )
803             delete pSelector;
804     }
805 
806     // '{'
807     if( CSS1_OBRACE != nToken )
808         return;
809     nToken = GetNextToken();
810 
811     // declaration
812     String aProperty;
813     CSS1Expression *pExpr = ParseDeclaration( aProperty );
814     if( !pExpr )
815         return;
816 
817     // expression verarbeiten
818     if( DeclarationParsed( aProperty, pExpr ) )
819         delete pExpr;
820 
821     LOOP_CHECK_RESTART
822 
823     // [ ';' declaration ]*
824     while( CSS1_SEMICOLON==nToken && IsParserWorking() )
825     {
826         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseRule()/declaration *" )
827 
828         // ';'
829         nToken = GetNextToken();
830 
831         // declaration
832         if( CSS1_IDENT == nToken )
833         {
834             CSS1Expression *pExp = ParseDeclaration( aProperty );
835             if( pExp )
836             {
837                 // expression verarbeiten
838                 if( DeclarationParsed( aProperty, pExp ) )
839                     delete pExp;
840             }
841         }
842     }
843 
844     // '}'
845     if( CSS1_CBRACE == nToken )
846         nToken = GetNextToken();
847 }
848 
849 // selector
850 //  : simple_selector+ [ ':' pseudo_element ]?
851 //
852 // simple_selector
853 //  : element_name [ DOT_WO_WS class ]?
854 //  | DOT_W_WS class
855 //  | id_selector
856 //
857 // element_name
858 //  : IDENT
859 //
860 // class
861 //  : IDENT
862 //
863 // id_selector
864 //  : '#' IDENT
865 //
866 // pseude_element
867 //  : IDENT
868 //
ParseSelector()869 CSS1Selector *CSS1Parser::ParseSelector()
870 {
871     CSS1Selector *pRoot = 0, *pLast = 0;
872 
873     sal_Bool bDone = sal_False;
874     CSS1Selector *pNew = 0;
875 
876     LOOP_CHECK_DECL
877 
878     // simple_selector+
879     while( !bDone && IsParserWorking() )
880     {
881         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseSelector()" )
882 
883         sal_Bool bNextToken = sal_True;
884 
885         switch( nToken )
886         {
887         case CSS1_IDENT:
888             {
889                 // element_name [ DOT_WO_WS class ]?
890 
891                 // element_name
892                 String aElement = aToken;
893                 CSS1SelectorType eType = CSS1_SELTYPE_ELEMENT;
894                 nToken = GetNextToken();
895 
896                 if( CSS1_DOT_WO_WS == nToken )
897                 {
898                     // DOT_WO_WS
899                     nToken = GetNextToken();
900 
901                     // class
902                     if( CSS1_IDENT == nToken )
903                     {
904                         (aElement += '.') += aToken;
905                         eType = CSS1_SELTYPE_ELEM_CLASS;
906                     }
907                     else
908                     {
909                         // class fehlt
910                         return pRoot;
911                     }
912                 }
913                 else
914                 {
915                     // das war jetzt ein Look-Ahead
916                     bNextToken = sal_False;
917                 }
918                 pNew = new CSS1Selector( eType, aElement );
919             }
920             break;
921         case CSS1_DOT_W_WS:
922             // DOT_W_WS class
923 
924             // DOT_W_WS
925             nToken = GetNextToken();
926 
927             if( CSS1_IDENT==nToken )
928             {
929                 // class
930                 pNew = new CSS1Selector( CSS1_SELTYPE_CLASS, aToken );
931             }
932             else
933             {
934                 // class fehlt
935                 return pRoot;
936             }
937             break;
938         case CSS1_HASH:
939             // '#' id_selector
940 
941             // '#'
942             nToken = GetNextToken();
943 
944             if( CSS1_IDENT==nToken )
945             {
946                 // id_selector
947                 pNew = new CSS1Selector( CSS1_SELTYPE_ID, aToken );
948             }
949             else
950             {
951                 // id_selector fehlt
952                 return pRoot;
953             }
954             break;
955 
956 // /Feature: PrintExt
957         case CSS1_PAGE_SYM:
958             {
959                 //  @page
960                 pNew = new CSS1Selector( CSS1_SELTYPE_PAGE, aToken );
961             }
962             break;
963 // /Feature: PrintExt
964 
965         default:
966             // wir wissen nicht was kommt, also aufhoehren
967             bDone = sal_True;
968             break;
969         }
970 
971         // falls ein Selektor angelegt wurd, ihn speichern
972         if( pNew )
973         {
974             DBG_ASSERT( (pRoot!=0) == (pLast!=0),
975                     "Root-Selektor, aber kein Last" );
976             if( pLast )
977                 pLast->SetNext( pNew );
978             else
979                 pRoot = pNew;
980 
981             pLast = pNew;
982             pNew = 0;
983         }
984 
985         if( bNextToken && !bDone )
986             nToken = GetNextToken();
987     }
988 
989     if( !pRoot )
990     {
991         // simple_selector fehlt
992         return pRoot;
993     }
994 
995     // [ ':' pseudo_element ]?
996     if( CSS1_COLON==nToken && IsParserWorking() )
997     {
998         // ':' pseudo element
999         nToken = GetNextToken();
1000         if( CSS1_IDENT==nToken )
1001         {
1002             pLast->SetNext( new CSS1Selector(CSS1_SELTYPE_PSEUDO,aToken) );
1003             nToken = GetNextToken();
1004         }
1005         else
1006         {
1007             // pseudo_element fehlt
1008             return pRoot;
1009         }
1010     }
1011 
1012     return pRoot;
1013 }
1014 
1015 // declaration
1016 //  : property ':' expr prio?
1017 //  | /* empty */
1018 //
1019 // expression
1020 //  : term [ operator term ]*
1021 //
1022 // term
1023 //  : unary_operator?
1024 //     [ NUMBER | STRING | PERCENTAGE | LENGTH | EMS | EXS | IDENT |
1025 //       HEXCOLOR | URL | RGB ]
1026 //
1027 // operator
1028 //  : '/' | ',' | /* empty */
1029 //
1030 // unary_operator
1031 //  : '-' | '+'
1032 //
1033 // property
1034 //  : ident
1035 //
1036 // das Vorzeichen wird nur fuer numerische Werte (ausser PERCENTAGE)
1037 // beruecksichtigt und wird auf nValue angewendet!
ParseDeclaration(String & rProperty)1038 CSS1Expression *CSS1Parser::ParseDeclaration( String& rProperty )
1039 {
1040     CSS1Expression *pRoot = 0, *pLast = 0;
1041 
1042     // property
1043     if( CSS1_IDENT != nToken )
1044     {
1045         // property fehlt
1046         return pRoot;
1047     }
1048     rProperty = aToken;
1049 
1050     nToken = GetNextToken();
1051 
1052 
1053     // ':'
1054     if( CSS1_COLON != nToken )
1055     {
1056         // ':' fehlt
1057         return pRoot;
1058     }
1059     nToken = GetNextToken();
1060 
1061     // term [operator term]*
1062     // hier sind wir sehr lax, was die Syntax angeht, sollte aber kein
1063     // Problem sein
1064     sal_Bool bDone = sal_False;
1065     sal_Unicode cSign = 0, cOp = 0;
1066     CSS1Expression *pNew = 0;
1067 
1068     LOOP_CHECK_DECL
1069 
1070     while( !bDone && IsParserWorking() )
1071     {
1072         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseDeclaration()" )
1073 
1074         switch( nToken )
1075         {
1076         case CSS1_MINUS:
1077             cSign = '-';
1078             break;
1079 
1080         case CSS1_PLUS:
1081             cSign = '+';
1082             break;
1083 
1084         case CSS1_NUMBER:
1085         case CSS1_LENGTH:
1086         case CSS1_PIXLENGTH:
1087         case CSS1_EMS:
1088         case CSS1_EMX:
1089             if( '-'==cSign )
1090                 nValue = -nValue;
1091         case CSS1_STRING:
1092         case CSS1_PERCENTAGE:
1093         case CSS1_IDENT:
1094         case CSS1_URL:
1095         case CSS1_RGB:
1096         case CSS1_HEXCOLOR:
1097             pNew = new CSS1Expression( nToken, aToken, nValue, cOp );
1098             nValue = 0; // sonst landet das auch im naechsten Ident
1099             cSign = 0;
1100             cOp = 0;
1101             break;
1102 
1103         case CSS1_SLASH:
1104             cOp = '/';
1105             cSign = 0;
1106             break;
1107 
1108         case CSS1_COMMA:
1109             cOp = ',';
1110             cSign = 0;
1111             break;
1112 
1113         default:
1114             bDone = sal_True;
1115             break;
1116         }
1117 
1118         // falls ein Expression angelegt wurde, diesen speichern
1119         if( pNew )
1120         {
1121             DBG_ASSERT( (pRoot!=0) == (pLast!=0),
1122                     "Root-Selektor, aber kein Last" );
1123             if( pLast )
1124                 pLast->SetNext( pNew );
1125             else
1126                 pRoot = pNew;
1127 
1128             pLast = pNew;
1129             pNew = 0;
1130         }
1131 
1132         if( !bDone )
1133             nToken = GetNextToken();
1134     }
1135 
1136     if( !pRoot )
1137     {
1138         // term fehlt
1139         return pRoot;
1140     }
1141 
1142     // prio?
1143     if( CSS1_IMPORTANT_SYM==nToken )
1144     {
1145         // IMPORTANT_SYM
1146         nToken = GetNextToken();
1147     }
1148 
1149     return pRoot;
1150 }
1151 
1152 /*  */
1153 
CSS1Parser()1154 CSS1Parser::CSS1Parser()
1155 {
1156 }
1157 
~CSS1Parser()1158 CSS1Parser::~CSS1Parser()
1159 {
1160 }
1161 
1162 /*  */
1163 
ParseStyleSheet(const String & rIn)1164 sal_Bool CSS1Parser::ParseStyleSheet( const String& rIn )
1165 {
1166     String aTmp( rIn );
1167 
1168     sal_Unicode c;
1169     while( aTmp.Len() &&
1170            ( ' '==(c=aTmp.GetChar(0)) || '\t'==c || '\r'==c || '\n'==c ) )
1171         aTmp.Erase( 0, 1 );
1172 
1173     while( aTmp.Len() && ( ' '==(c=aTmp.GetChar( aTmp.Len()-1))
1174            || '\t'==c || '\r'==c || '\n'==c ) )
1175         aTmp.Erase( aTmp.Len()-1 );
1176 
1177     // SGML-Kommentare entfernen
1178     if( aTmp.Len() >= 4 &&
1179         aTmp.CompareToAscii("<!--",4) == COMPARE_EQUAL )
1180         aTmp.Erase( 0, 4 );
1181 
1182     if( aTmp.Len() >=3 &&
1183         aTmp.Copy(aTmp.Len()-3).CompareToAscii("-->") == COMPARE_EQUAL )
1184         aTmp.Erase( aTmp.Len()-3 );
1185 
1186     if( !aTmp.Len() )
1187         return sal_True;
1188 
1189     InitRead( aTmp );
1190 
1191     ParseStyleSheet();
1192 
1193     return sal_True;
1194 }
1195 
1196 
ParseStyleOption(const String & rIn)1197 sal_Bool CSS1Parser::ParseStyleOption( const String& rIn )
1198 {
1199     if( !rIn.Len() )
1200         return sal_True;
1201 
1202     InitRead( rIn );
1203 
1204     String aProperty;
1205     CSS1Expression *pExpr = ParseDeclaration( aProperty );
1206     if( !pExpr )
1207     {
1208         return sal_False;
1209     }
1210 
1211     // expression verarbeiten
1212     if( DeclarationParsed( aProperty, pExpr ) )
1213         delete pExpr;
1214 
1215     LOOP_CHECK_DECL
1216 
1217     // [ ';' declaration ]*
1218     while( CSS1_SEMICOLON==nToken && IsParserWorking() )
1219     {
1220         LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleOption()" )
1221 
1222         nToken = GetNextToken();
1223         if( CSS1_IDENT==nToken )
1224         {
1225             CSS1Expression *pExp = ParseDeclaration( aProperty );
1226             if( pExp )
1227             {
1228                 // expression verarbeiten
1229                 if( DeclarationParsed( aProperty, pExp ) )
1230                     delete pExp;
1231             }
1232         }
1233     }
1234 
1235     return sal_True;
1236 }
1237 
SelectorParsed(const CSS1Selector *,sal_Bool)1238 sal_Bool CSS1Parser::SelectorParsed( const CSS1Selector * /* pSelector */, sal_Bool /*bFirst*/ )
1239 {
1240     // Selektor loeschen
1241     return sal_True;
1242 }
1243 
DeclarationParsed(const String &,const CSS1Expression *)1244 sal_Bool CSS1Parser::DeclarationParsed( const String& /*rProperty*/,
1245                                     const CSS1Expression * /* pExpr */ )
1246 {
1247     // Deklaration loeschen
1248     return sal_True;
1249 }
1250 
1251 
1252 /*  */
1253 
~CSS1Selector()1254 CSS1Selector::~CSS1Selector()
1255 {
1256     delete pNext;
1257 }
1258 
1259 /*  */
1260 
~CSS1Expression()1261 CSS1Expression::~CSS1Expression()
1262 {
1263     delete pNext;
1264 }
1265 
GetURL(String & rURL) const1266 sal_Bool CSS1Expression::GetURL( String& rURL  ) const
1267 {
1268     DBG_ASSERT( CSS1_URL==eType, "CSS1-Ausruck ist keine Farbe URL" );
1269 
1270     DBG_ASSERT( aValue.CompareIgnoreCaseToAscii( sCSS1_url, 3 ) ==
1271                                         COMPARE_EQUAL &&
1272                 aValue.Len() > 5 &&
1273                 '(' == aValue.GetChar(3) &&
1274                 ')' == aValue.GetChar(aValue.Len()-1),
1275                 "keine gueltiges URL(...)" );
1276 
1277     sal_Bool bRet = sal_False;
1278 
1279     if( aValue.Len() > 5 )
1280     {
1281         rURL = aValue.Copy( 4, aValue.Len()-5 );
1282         rURL.EraseTrailingChars();
1283         rURL.EraseLeadingChars();
1284         bRet = sal_True;
1285     }
1286 
1287     return bRet;
1288 }
1289 
GetColor(Color & rColor) const1290 sal_Bool CSS1Expression::GetColor( Color &rColor ) const
1291 {
1292     DBG_ASSERT( CSS1_IDENT==eType || CSS1_RGB==eType ||
1293                 CSS1_HEXCOLOR==eType || CSS1_STRING==eType,
1294                 "CSS1-Ausruck kann keine Farbe sein" );
1295 
1296     sal_Bool bRet = sal_False;
1297     sal_uLong nColor = ULONG_MAX;
1298 
1299     switch( eType )
1300     {
1301     case CSS1_RGB:
1302         {
1303             sal_uInt8 aColors[3] = { 0, 0, 0 };
1304 
1305             DBG_ASSERT( aValue.CompareIgnoreCaseToAscii( sCSS1_rgb, 3 )
1306                                             == COMPARE_EQUAL &&
1307                         aValue.Len() > 5 &&
1308                         '(' == aValue.GetChar( 3 ) &&
1309                         ')' == aValue.GetChar( aValue.Len()-1),
1310                         "keine gueltiges RGB(...)" );
1311 
1312             String aColorStr( aValue.Copy( 4, aValue.Len()-1 ) );
1313 
1314             xub_StrLen nPos = 0;
1315             sal_uInt16 nCol = 0;
1316 
1317             while( nCol < 3 && nPos < aColorStr.Len() )
1318             {
1319                 sal_Unicode c;
1320                 while( nPos < aColorStr.Len() &&
1321                         ((c=aColorStr.GetChar(nPos)) == ' ' || c == '\t' ||
1322                         c == '\n' || c== '\r' ) )
1323                     nPos++;
1324 
1325                 xub_StrLen nEnd = aColorStr.Search( ',', nPos );
1326                 String aNumber;
1327                 if( STRING_NOTFOUND==nEnd )
1328                 {
1329                     aNumber = aColorStr.Copy(nPos);
1330                     nPos = aColorStr.Len();
1331                 }
1332                 else
1333                 {
1334                     aNumber = aColorStr.Copy( nPos, nEnd-nPos );
1335                     nPos = nEnd+1;
1336                 }
1337 
1338                 sal_uInt16 nNumber = (sal_uInt16)aNumber.ToInt32();
1339                 if( aNumber.Search('%') != STRING_NOTFOUND )
1340                 {
1341                     if( nNumber > 100 )
1342                         nNumber = 100;
1343                     nNumber *= 255;
1344                     nNumber /= 100;
1345                 }
1346                 else if( nNumber > 255 )
1347                     nNumber = 255;
1348 
1349                 aColors[nCol] = (sal_uInt8)nNumber;
1350                 nCol ++;
1351             }
1352 
1353             rColor.SetRed( aColors[0] );
1354             rColor.SetGreen( aColors[1] );
1355             rColor.SetBlue( aColors[2] );
1356 
1357             bRet = sal_True;    // etwas anderes als eine Farbe kann es nicht sein
1358         }
1359         break;
1360 
1361     case CSS1_IDENT:
1362     case CSS1_STRING:
1363         {
1364             String aTmp( aValue );
1365             aTmp.ToUpperAscii();
1366             nColor = GetHTMLColor( aTmp );
1367             bRet = nColor != ULONG_MAX;
1368         }
1369         if( bRet || CSS1_STRING != eType || !aValue.Len() ||
1370             aValue.GetChar( 0 )!='#' )
1371             break;
1372 
1373     case CSS1_HEXCOLOR:
1374         {
1375             // HACK fuer MS-IE: DIe Farbe kann auch in einem String stehen
1376             xub_StrLen nOffset = CSS1_STRING==eType ? 1 : 0;
1377             sal_Bool bDouble = aValue.Len()-nOffset == 3;
1378             xub_StrLen i = nOffset, nEnd = (bDouble ? 3 : 6) + nOffset;
1379 
1380             nColor = 0;
1381             for( ; i<nEnd; i++ )
1382             {
1383                 sal_Unicode c = (i<aValue.Len() ? aValue.GetChar(i)
1384                                                          : '0' );
1385                 if( c >= '0' && c <= '9' )
1386                     c -= 48;
1387                 else if( c >= 'A' && c <= 'F' )
1388                     c -= 55;
1389                 else if( c >= 'a' && c <= 'f' )
1390                     c -= 87;
1391                 else
1392                     c = 16;
1393 
1394                 nColor *= 16;
1395                 if( c<16 )
1396                     nColor += c;
1397                 if( bDouble )
1398                 {
1399                     nColor *= 16;
1400                     if( c<16 )
1401                         nColor += c;
1402                 }
1403             }
1404             // bRet = i==6;
1405             bRet = sal_True;
1406         }
1407         break;
1408     default:
1409         ;
1410     }
1411 
1412 
1413     if( bRet && nColor!=ULONG_MAX )
1414     {
1415         rColor.SetRed( (sal_uInt8)((nColor & 0x00ff0000UL) >> 16) );
1416         rColor.SetGreen( (sal_uInt8)((nColor & 0x0000ff00UL) >> 8) );
1417         rColor.SetBlue( (sal_uInt8)(nColor & 0x000000ffUL) );
1418     }
1419 
1420     return bRet;
1421 }
1422 
1423