merge the formfield patch from ooo-build
[ooovba.git] / sw / source / filter / html / parcss1.cxx
blob40bb93ee15c08af847e16a15acf5a2230ac97aa5
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: parcss1.cxx,v $
10 * $Revision: 1.10 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <limits.h>
39 #include <rtl/ustrbuf.hxx>
40 #include <tools/debug.hxx>
41 #include <vcl/svapp.hxx>
42 #include <svtools/htmltokn.h>
44 #include "css1kywd.hxx"
45 #include "parcss1.hxx"
48 // Loop-Check: Um Endlos-Schleifen zu vermeiden, wird in jeder
49 // Schalife geprueft, ob ein Fortschritt in der Eingabe-Position
50 // stattgefunden hat
51 #define LOOP_CHECK
53 #ifdef LOOP_CHECK
55 #define LOOP_CHECK_DECL \
56 xub_StrLen nOldInPos = STRING_MAXLEN;
57 #define LOOP_CHECK_RESTART \
58 nOldInPos = STRING_MAXLEN;
59 #define LOOP_CHECK_CHECK( where ) \
60 DBG_ASSERT( nOldInPos!=nInPos || cNextCh==(sal_Unicode)EOF, where ); \
61 if( nOldInPos==nInPos && cNextCh!=(sal_Unicode)EOF ) \
62 break; \
63 else \
64 nOldInPos = nInPos;
66 #else
68 #define LOOP_CHECK_DECL
69 #define LOOP_CHECK_RESTART
70 #define LOOP_CHECK_CHECK( where )
72 #endif
76 const sal_Int32 MAX_LEN = 1024;
78 /* \f */
80 void CSS1Parser::InitRead( const String& rIn )
82 nlLineNr = 0;
83 nlLinePos = 0;
85 bWhiteSpace = TRUE; // Wenn noch nichts gelesen wurde ist das wie WS
86 bEOF = FALSE;
87 eState = CSS1_PAR_WORKING;
88 nValue = 0.;
90 aIn = rIn;
91 nInPos = 0;
92 cNextCh = GetNextChar();
93 nToken = GetNextToken();
96 sal_Unicode CSS1Parser::GetNextChar()
98 if( nInPos >= aIn.Len() )
100 bEOF = TRUE;
101 return (sal_Unicode)EOF;
104 sal_Unicode c = aIn.GetChar( nInPos );
105 nInPos++;
107 if( c == '\n' )
109 IncLineNr();
110 SetLinePos( 1L );
112 else
113 IncLinePos();
115 return c;
118 /* \f */
120 // Diese Funktion realisiert den in
122 // http://www.w3.orh/pub/WWW/TR/WD-css1.html
123 // bzw. http://www.w3.orh/pub/WWW/TR/WD-css1-960220.html
125 // beschriebenen Scanner fuer CSS1. Es handelt sich um eine direkte
126 // Umsetzung der dort beschriebenen Lex-Grammatik
128 CSS1Token CSS1Parser::GetNextToken()
130 CSS1Token nRet = CSS1_NULL;
131 aToken.Erase();
133 do {
134 // Merken, ob davor White-Space gelesen wurde
135 BOOL bPrevWhiteSpace = bWhiteSpace;
136 bWhiteSpace = FALSE;
138 BOOL bNextCh = TRUE;
139 switch( cNextCh )
141 case '/': // COMMENT | '/'
143 cNextCh = GetNextChar();
144 if( '*' == cNextCh )
146 // COMMENT
147 cNextCh = GetNextChar();
149 BOOL bAsterix = FALSE;
150 while( !(bAsterix && '/'==cNextCh) && !IsEOF() )
152 bAsterix = ('*'==cNextCh);
153 cNextCh = GetNextChar();
156 else
158 // '/'
159 bNextCh = FALSE;
160 nRet = CSS1_SLASH;
163 break;
165 case '@': // '@import' | '@XXX'
167 cNextCh = GetNextChar();
168 if( ('A' <= cNextCh && cNextCh <= 'Z') ||
169 ('a' <= cNextCh && cNextCh <= 'z') )
171 // den naechsten Identifer scannen
172 ::rtl::OUStringBuffer sTmpBuffer( 32L );
173 do {
174 sTmpBuffer.append( cNextCh );
175 cNextCh = GetNextChar();
176 } while( ('A' <= cNextCh && cNextCh <= 'Z') ||
177 ('a' <= cNextCh && cNextCh <= 'z') ||
178 ('0' <= cNextCh && cNextCh <= '9') ||
179 '-'==cNextCh && !IsEOF() );
181 aToken += String(sTmpBuffer.makeStringAndClear());
183 // und schauen, ob wir ihn kennen
184 switch( aToken.GetChar(0) )
186 case 'i':
187 case 'I':
188 if( aToken.EqualsIgnoreCaseAscii(sCSS1_import) )
189 nRet = CSS1_IMPORT_SYM;
190 break;
191 // /Feature: PrintExt
192 case 'p':
193 case 'P':
194 if( aToken.EqualsIgnoreCaseAscii(sCSS1_page) )
195 nRet = CSS1_PAGE_SYM;
196 break;
197 // /Feature: PrintExt
200 // Fehlerbehandlung: '@ident' und alles bis
201 // zu einem Semikolon der dem Ende des folgenden
202 // Blocks ignorieren
203 if( CSS1_NULL==nRet )
205 aToken.Erase();
206 USHORT nBlockLvl = 0;
207 sal_Unicode cQuoteCh = 0;
208 BOOL bDone = FALSE, bEscape = FALSE;
209 while( !bDone && !IsEOF() )
211 BOOL bOldEscape = bEscape;
212 bEscape = FALSE;
213 switch( cNextCh )
215 case '{':
216 if( !cQuoteCh && !bOldEscape )
217 nBlockLvl++;;
218 break;
219 case ';':
220 if( !cQuoteCh && !bOldEscape )
221 bDone = nBlockLvl==0;
222 break;
223 case '}':
224 if( !cQuoteCh && !bOldEscape )
225 bDone = --nBlockLvl==0;
226 break;
227 case '\"':
228 case '\'':
229 if( !bOldEscape )
231 if( cQuoteCh )
233 if( cQuoteCh == cNextCh )
234 cQuoteCh = 0;
236 else
238 cQuoteCh = cNextCh;
241 break;
242 case '\\':
243 if( !bOldEscape )
244 bEscape = TRUE;
245 break;
247 cNextCh = GetNextChar();
251 bNextCh = FALSE;
254 break;
256 case '!': // '!' 'legal' | '!' 'important' | syntax error
258 // White Space ueberlesen
259 cNextCh = GetNextChar();
260 while( ( ' ' == cNextCh ||
261 (cNextCh >= 0x09 && cNextCh <= 0x0d) ) && !IsEOF() )
263 bWhiteSpace = TRUE;
264 cNextCh = GetNextChar();
267 if( 'i'==cNextCh || 'I'==cNextCh)
269 // den naechsten Identifer scannen
270 ::rtl::OUStringBuffer sTmpBuffer( 32L );
271 do {
272 sTmpBuffer.append( cNextCh );
273 cNextCh = GetNextChar();
274 } while( ('A' <= cNextCh && cNextCh <= 'Z') ||
275 ('a' <= cNextCh && cNextCh <= 'z') ||
276 ('0' <= cNextCh && cNextCh <= '9') ||
277 '-' == cNextCh && !IsEOF() );
279 aToken += String(sTmpBuffer.makeStringAndClear());
281 if( ('i'==aToken.GetChar(0) || 'I'==aToken.GetChar(0)) &&
282 aToken.EqualsIgnoreCaseAscii(sCSS1_important) )
284 // '!' 'important'
285 nRet = CSS1_IMPORTANT_SYM;
287 else
289 // Fehlerbehandlung: '!' ignorieren, IDENT nicht
290 nRet = CSS1_IDENT;
293 bWhiteSpace = FALSE;
294 bNextCh = FALSE;
296 else
298 // Fehlerbehandlung: '!' ignorieren
299 bNextCh = FALSE;
302 break;
304 case '\"':
305 case '\'': // STRING
307 // \... geht noch nicht!!!
308 sal_Unicode cQuoteChar = cNextCh;
309 cNextCh = GetNextChar();
311 ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
312 do {
313 sTmpBuffer.append( cNextCh );
314 cNextCh = GetNextChar();
315 } while( cQuoteChar != cNextCh && !IsEOF() );
317 aToken += String(sTmpBuffer.makeStringAndClear());
319 nRet = CSS1_STRING;
321 break;
323 case '0':
324 case '1':
325 case '2':
326 case '3':
327 case '4':
328 case '5':
329 case '6':
330 case '7':
331 case '8':
332 case '9': // NUMBER | PERCENTAGE | LENGTH
334 // die aktuelle Position retten
335 xub_StrLen nInPosSave = nInPos;
336 sal_Unicode cNextChSave = cNextCh;
337 sal_uInt32 nlLineNrSave = nlLineNr;
338 sal_uInt32 nlLinePosSave = nlLinePos;
339 BOOL bEOFSave = bEOF;
341 // erstmal versuchen eine Hex-Zahl zu scannen
342 ::rtl::OUStringBuffer sTmpBuffer( 16 );
343 do {
344 sTmpBuffer.append( cNextCh );
345 cNextCh = GetNextChar();
346 } while( sTmpBuffer.getLength() < 7 &&
347 ( ('0'<=cNextCh && '9'>=cNextCh) ||
348 ('A'<=cNextCh && 'F'>=cNextCh) ||
349 ('a'<=cNextCh && 'f'>=cNextCh) ) &&
350 !IsEOF() );
352 if( sTmpBuffer.getLength()==6 )
354 // wir haben eine hexadezimale Farbe gefunden
355 aToken += String(sTmpBuffer.makeStringAndClear());
356 nRet = CSS1_HEXCOLOR;
357 bNextCh = FALSE;
359 break;
362 // sonst versuchen wir es mit einer Zahl
363 nInPos = nInPosSave;
364 cNextCh = cNextChSave;
365 nlLineNr = nlLineNrSave;
366 nlLinePos = nlLinePosSave;
367 bEOF = bEOFSave;
369 // erstmal die Zahl scannen
370 sTmpBuffer.setLength( 0L );
371 do {
372 sTmpBuffer.append( cNextCh );
373 cNextCh = GetNextChar();
374 } while( (('0'<=cNextCh && '9'>=cNextCh) || '.'==cNextCh) &&
375 !IsEOF() );
377 aToken += String(sTmpBuffer.makeStringAndClear());
378 nValue = aToken.ToDouble();
380 // White Space ueberlesen
381 while( ( ' ' == cNextCh ||
382 (cNextCh >= 0x09 && cNextCh <= 0x0d) ) && !IsEOF() )
384 bWhiteSpace = TRUE;
385 cNextCh = GetNextChar();
388 // und nun Schauen, ob es eine Einheit gibt
389 switch( cNextCh )
391 case '%': // PERCENTAGE
392 bWhiteSpace = FALSE;
393 nRet = CSS1_PERCENTAGE;
394 break;
396 case 'c':
397 case 'C': // LENGTH cm | LENGTH IDENT
398 case 'e':
399 case 'E': // LENGTH (em | ex) | LENGTH IDENT
400 case 'i':
401 case 'I': // LENGTH inch | LENGTH IDENT
402 case 'p':
403 case 'P': // LENGTH (pt | px | pc) | LENGTH IDENT
404 case 'm':
405 case 'M': // LENGTH mm | LENGTH IDENT
407 // die aktuelle Position retten
408 xub_StrLen nInPosOld = nInPos;
409 sal_Unicode cNextChOld = cNextCh;
410 ULONG nlLineNrOld = nlLineNr;
411 ULONG nlLinePosOld = nlLinePos;
412 BOOL bEOFOld = bEOF;
414 // den naechsten Identifer scannen
415 String aIdent;
416 ::rtl::OUStringBuffer sTmpBuffer2( 64L );
417 do {
418 sTmpBuffer2.append( cNextCh );
419 cNextCh = GetNextChar();
420 } while( ( ('A' <= cNextCh && cNextCh <= 'Z') ||
421 ('a' <= cNextCh && cNextCh <= 'z') ||
422 ('0' <= cNextCh && cNextCh <= '9') ||
423 '-'==cNextCh) && !IsEOF() );
425 aIdent += String(sTmpBuffer2.makeStringAndClear());
427 // Ist es eine Einheit?
428 const sal_Char *pCmp1 = 0, *pCmp2 = 0, *pCmp3 = 0;
429 double nScale1 = 1., nScale2 = 1., nScale3 = 1.;
430 CSS1Token nToken1 = CSS1_LENGTH,
431 nToken2 = CSS1_LENGTH,
432 nToken3 = CSS1_LENGTH;
433 switch( aIdent.GetChar(0) )
435 case 'c':
436 case 'C':
437 pCmp1 = sCSS1_UNIT_cm;
438 nScale1 = (72.*20.)/2.54; // twip
439 break;
440 case 'e':
441 case 'E':
442 pCmp1 = sCSS1_UNIT_em;
443 nToken1 = CSS1_EMS;
445 pCmp2 = sCSS1_UNIT_ex;
446 nToken2 = CSS1_EMX;
447 break;
448 case 'i':
449 case 'I':
450 pCmp1 = sCSS1_UNIT_inch;
451 nScale1 = 72.*20.; // twip
452 break;
453 case 'm':
454 case 'M':
455 pCmp1 = sCSS1_UNIT_mm;
456 nScale1 = (72.*20.)/25.4; // twip
457 break;
458 case 'p':
459 case 'P':
460 pCmp1 = sCSS1_UNIT_pt;
461 nScale1 = 20.; // twip
463 pCmp2 = sCSS1_UNIT_pc;
464 nScale2 = 12.*20.; // twip
466 pCmp3 = sCSS1_UNIT_px;
467 nToken3 = CSS1_PIXLENGTH;
468 break;
471 double nScale = 0.0;
472 DBG_ASSERT( pCmp1, "Wo kommt das erste Zeichen her?" );
473 if( aIdent.EqualsIgnoreCaseAscii(pCmp1) )
475 nScale = nScale1;
476 nRet = nToken1;
478 else if( pCmp2 &&
479 aIdent.EqualsIgnoreCaseAscii(pCmp2) )
481 nScale = nScale2;
482 nRet = nToken2;
484 else if( pCmp3 &&
485 aIdent.EqualsIgnoreCaseAscii(pCmp3) )
487 nScale = nScale3;
488 nRet = nToken3;
490 else
492 nRet = CSS1_NUMBER;
495 if( CSS1_LENGTH==nRet && nScale!=1.0 )
496 nValue *= nScale;
498 if( nRet == CSS1_NUMBER )
500 nInPos = nInPosOld;
501 cNextCh = cNextChOld;
502 nlLineNr = nlLineNrOld;
503 nlLinePos = nlLinePosOld;
504 bEOF = bEOFOld;
506 else
508 bWhiteSpace = FALSE;
510 bNextCh = FALSE;
512 break;
513 default: // NUMBER IDENT
514 bNextCh = FALSE;
515 nRet = CSS1_NUMBER;
516 break;
519 break;
521 case ':': // ':'
522 // link/visited/active abfangen !!!
523 nRet = CSS1_COLON;
524 break;
526 case '.': // DOT_W_WS | DOT_WO_WS
527 nRet = bPrevWhiteSpace ? CSS1_DOT_W_WS : CSS1_DOT_WO_WS;
528 break;
530 // case '/': siehe oben
532 case '+': // '+'
533 nRet = CSS1_PLUS;
534 break;
536 case '-': // '-'
537 nRet = CSS1_MINUS;
538 break;
540 case '{': // '{'
541 nRet = CSS1_OBRACE;
542 break;
544 case '}': // '}'
545 nRet = CSS1_CBRACE;
546 break;
548 case ';': // ';'
549 nRet = CSS1_SEMICOLON;
550 break;
552 case ',': // ','
553 nRet = CSS1_COMMA;
554 break;
556 case '#': // '#'
557 cNextCh = GetNextChar();
558 if( ('0'<=cNextCh && '9'>=cNextCh) ||
559 ('a'<=cNextCh && 'f'>=cNextCh) ||
560 ('A'<=cNextCh && 'F'>=cNextCh) )
562 // die aktuelle Position retten
563 xub_StrLen nInPosSave = nInPos;
564 sal_Unicode cNextChSave = cNextCh;
565 ULONG nlLineNrSave = nlLineNr;
566 ULONG nlLinePosSave = nlLinePos;
567 BOOL bEOFSave = bEOF;
569 // erstmal versuchen eine Hex-Zahl zu scannen
570 ::rtl::OUStringBuffer sTmpBuffer( 6L );
571 do {
572 sTmpBuffer.append( cNextCh );
573 cNextCh = GetNextChar();
574 } while( sTmpBuffer.getLength() < 7 &&
575 ( ('0'<=cNextCh && '9'>=cNextCh) ||
576 ('A'<=cNextCh && 'F'>=cNextCh) ||
577 ('a'<=cNextCh && 'f'>=cNextCh) ) &&
578 !IsEOF() );
580 if( sTmpBuffer.getLength()==6 || sTmpBuffer.getLength()==3 )
582 // wir haben eine hexadezimale Farbe gefunden
583 aToken += String(sTmpBuffer.makeStringAndClear());
584 nRet = CSS1_HEXCOLOR;
585 bNextCh = FALSE;
587 break;
590 // sonst versuchen wir es mit einer Zahl
591 nInPos = nInPosSave;
592 cNextCh = cNextChSave;
593 nlLineNr = nlLineNrSave;
594 nlLinePos = nlLinePosSave;
595 bEOF = bEOFSave;
598 nRet = CSS1_HASH;
599 bNextCh = FALSE;
600 break;
602 case ' ':
603 case '\t':
604 case '\r':
605 case '\n': // White-Space
606 bWhiteSpace = TRUE;
607 break;
609 case (sal_Unicode)EOF:
610 if( IsEOF() )
612 eState = CSS1_PAR_ACCEPTED;
613 bNextCh = FALSE;
614 break;
616 // kein break;
618 default: // IDENT | syntax error
619 // TODO IsAlpha
620 if( ('A' <= cNextCh && cNextCh <= 'Z') ||
621 ('a' <= cNextCh && cNextCh <= 'z') )
623 // IDENT
625 BOOL bHexColor = TRUE;
627 // den naechsten Identifer scannen
628 ::rtl::OUStringBuffer sTmpBuffer( 64L );
629 do {
630 sTmpBuffer.append( cNextCh );
631 if( bHexColor )
633 bHexColor = sTmpBuffer.getLength()<7 &&
634 ( ('0'<=cNextCh && '9'>=cNextCh) ||
635 ('A'<=cNextCh && 'F'>=cNextCh) ||
636 ('a'<=cNextCh && 'f'>=cNextCh) );
638 cNextCh = GetNextChar();
639 // TODO: AlphaNumeric
640 } while( ( ('0'<=cNextCh && '9'>=cNextCh) ||
641 ('A'<=cNextCh && 'Z'>=cNextCh) ||
642 ('a'<=cNextCh && 'z'>=cNextCh) ||
643 '-'==cNextCh ) &&
644 !IsEOF() );
646 aToken += String(sTmpBuffer.makeStringAndClear());
648 if( bHexColor && sTmpBuffer.getLength()==6 )
650 bNextCh = FALSE;
651 nRet = CSS1_HEXCOLOR;
653 break;
655 if( '('==cNextCh &&
656 ( (('u'==aToken.GetChar(0) || 'U'==aToken.GetChar(0)) &&
657 aToken.EqualsIgnoreCaseAscii(sCSS1_url)) ||
658 (('r'==aToken.GetChar(0) || 'R'==aToken.GetChar(0)) &&
659 aToken.EqualsIgnoreCaseAscii(sCSS1_rgb)) ) )
661 USHORT nNestCnt = 0;
662 ::rtl::OUStringBuffer sTmpBuffer2( 64L );
663 do {
664 sTmpBuffer2.append( cNextCh );
665 switch( cNextCh )
667 case '(': nNestCnt++; break;
668 case ')': nNestCnt--; break;
670 cNextCh = GetNextChar();
671 } while( (nNestCnt>1 || ')'!=cNextCh) && !IsEOF() );
672 sTmpBuffer2.append( cNextCh );
673 aToken += String(sTmpBuffer2.makeStringAndClear());
674 bNextCh = TRUE;
675 nRet = 'u'==aToken.GetChar(0) || 'U'==aToken.GetChar(0)
676 ? CSS1_URL
677 : CSS1_RGB;
679 else
681 bNextCh = FALSE;
682 nRet = CSS1_IDENT;
685 // Fehlerbehandlung: Zeichen ignorieren
686 break;
688 if( bNextCh )
689 cNextCh = GetNextChar();
691 } while( CSS1_NULL==nRet && IsParserWorking() );
693 return nRet;
697 /* \f */
700 // Dies folegenden Funktionen realisieren den in
702 // http://www.w3.orh/pub/WWW/TR/WD-css1.html
703 // bzw. http://www.w3.orh/pub/WWW/TR/WD-css1-960220.html
705 // beschriebenen Parser fuer CSS1. Es handelt sich um eine direkte
706 // Umsetzung der dort beschriebenen Grammatik
708 // stylesheet
709 // : import* rule*
711 // import
712 // : IMPORT_SYM url
714 // url
715 // : STRING
717 void CSS1Parser::ParseStyleSheet()
719 LOOP_CHECK_DECL
721 // import*
722 BOOL bDone = FALSE;
723 while( !bDone && IsParserWorking() )
725 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleSheet()/import *" )
727 switch( nToken )
729 case CSS1_IMPORT_SYM:
730 // IMPORT_SYM url
731 // url ueberspringen wir ungeprueft
732 nToken = GetNextToken();
733 break;
734 case CSS1_IDENT: // Look-Aheads
735 case CSS1_DOT_W_WS:
736 case CSS1_HASH:
737 // /Feature: PrintExt
738 case CSS1_PAGE_SYM:
739 // /Feature: PrintExt
740 // rule
741 bDone = TRUE;
742 break;
743 default:
744 // Fehlerbehandlung: ueberlesen
745 break;
748 if( !bDone )
749 nToken = GetNextToken();
752 LOOP_CHECK_RESTART
754 // rule *
755 while( IsParserWorking() )
757 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleSheet()/rule *" )
759 switch( nToken )
761 case CSS1_IDENT: // Look-Aheads
762 case CSS1_DOT_W_WS:
763 case CSS1_HASH:
764 // /Feature: PrintExt
765 case CSS1_PAGE_SYM:
766 // /Feature: PrintExt
767 // rule
768 ParseRule();
769 break;
770 default:
771 // Fehlerbehandlung: ueberlesen
772 nToken = GetNextToken();
773 break;
778 // rule
779 // : selector [ ',' selector ]*
780 // '{' declaration [ ';' declaration ]* '}'
782 void CSS1Parser::ParseRule()
784 // selector
785 CSS1Selector *pSelector = ParseSelector();
786 if( !pSelector )
787 return;
789 // Selektor verarbeiten
790 if( SelectorParsed( pSelector, TRUE ) )
791 delete pSelector;
793 LOOP_CHECK_DECL
795 // [ ',' selector ]*
796 while( CSS1_COMMA==nToken && IsParserWorking() )
798 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseRule()/selector *" )
800 // ',' ueberelesen
801 nToken = GetNextToken();
803 // selector
804 pSelector = ParseSelector();
805 if( !pSelector )
806 return;
808 // Selektor verarbeiten
809 if( SelectorParsed( pSelector, FALSE ) )
810 delete pSelector;
813 // '{'
814 if( CSS1_OBRACE != nToken )
815 return;
816 nToken = GetNextToken();
818 // declaration
819 String aProperty;
820 CSS1Expression *pExpr = ParseDeclaration( aProperty );
821 if( !pExpr )
822 return;
824 // expression verarbeiten
825 if( DeclarationParsed( aProperty, pExpr ) )
826 delete pExpr;
828 LOOP_CHECK_RESTART
830 // [ ';' declaration ]*
831 while( CSS1_SEMICOLON==nToken && IsParserWorking() )
833 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseRule()/declaration *" )
835 // ';'
836 nToken = GetNextToken();
838 // declaration
839 if( CSS1_IDENT == nToken )
841 CSS1Expression *pExp = ParseDeclaration( aProperty );
842 if( pExp )
844 // expression verarbeiten
845 if( DeclarationParsed( aProperty, pExp ) )
846 delete pExp;
851 // '}'
852 if( CSS1_CBRACE == nToken )
853 nToken = GetNextToken();
856 // selector
857 // : simple_selector+ [ ':' pseudo_element ]?
859 // simple_selector
860 // : element_name [ DOT_WO_WS class ]?
861 // | DOT_W_WS class
862 // | id_selector
864 // element_name
865 // : IDENT
867 // class
868 // : IDENT
870 // id_selector
871 // : '#' IDENT
873 // pseude_element
874 // : IDENT
876 CSS1Selector *CSS1Parser::ParseSelector()
878 CSS1Selector *pRoot = 0, *pLast = 0;
880 BOOL bDone = FALSE;
881 CSS1Selector *pNew = 0;
883 LOOP_CHECK_DECL
885 // simple_selector+
886 while( !bDone && IsParserWorking() )
888 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseSelector()" )
890 BOOL bNextToken = TRUE;
892 switch( nToken )
894 case CSS1_IDENT:
896 // element_name [ DOT_WO_WS class ]?
898 // element_name
899 String aElement = aToken;
900 CSS1SelectorType eType = CSS1_SELTYPE_ELEMENT;
901 nToken = GetNextToken();
903 if( CSS1_DOT_WO_WS == nToken )
905 // DOT_WO_WS
906 nToken = GetNextToken();
908 // class
909 if( CSS1_IDENT == nToken )
911 (aElement += '.') += aToken;
912 eType = CSS1_SELTYPE_ELEM_CLASS;
914 else
916 // class fehlt
917 return pRoot;
920 else
922 // das war jetzt ein Look-Ahead
923 bNextToken = FALSE;
925 pNew = new CSS1Selector( eType, aElement );
927 break;
928 case CSS1_DOT_W_WS:
929 // DOT_W_WS class
931 // DOT_W_WS
932 nToken = GetNextToken();
934 if( CSS1_IDENT==nToken )
936 // class
937 pNew = new CSS1Selector( CSS1_SELTYPE_CLASS, aToken );
939 else
941 // class fehlt
942 return pRoot;
944 break;
945 case CSS1_HASH:
946 // '#' id_selector
948 // '#'
949 nToken = GetNextToken();
951 if( CSS1_IDENT==nToken )
953 // id_selector
954 pNew = new CSS1Selector( CSS1_SELTYPE_ID, aToken );
956 else
958 // id_selector fehlt
959 return pRoot;
961 break;
963 // /Feature: PrintExt
964 case CSS1_PAGE_SYM:
966 // @page
967 pNew = new CSS1Selector( CSS1_SELTYPE_PAGE, aToken );
969 break;
970 // /Feature: PrintExt
972 default:
973 // wir wissen nicht was kommt, also aufhoehren
974 bDone = TRUE;
975 break;
978 // falls ein Selektor angelegt wurd, ihn speichern
979 if( pNew )
981 DBG_ASSERT( (pRoot!=0) == (pLast!=0),
982 "Root-Selektor, aber kein Last" );
983 if( pLast )
984 pLast->SetNext( pNew );
985 else
986 pRoot = pNew;
988 pLast = pNew;
989 pNew = 0;
992 if( bNextToken && !bDone )
993 nToken = GetNextToken();
996 if( !pRoot )
998 // simple_selector fehlt
999 return pRoot;
1002 // [ ':' pseudo_element ]?
1003 if( CSS1_COLON==nToken && IsParserWorking() )
1005 // ':' pseudo element
1006 nToken = GetNextToken();
1007 if( CSS1_IDENT==nToken )
1009 pLast->SetNext( new CSS1Selector(CSS1_SELTYPE_PSEUDO,aToken) );
1010 nToken = GetNextToken();
1012 else
1014 // pseudo_element fehlt
1015 return pRoot;
1019 return pRoot;
1022 // declaration
1023 // : property ':' expr prio?
1024 // | /* empty */
1026 // expression
1027 // : term [ operator term ]*
1029 // term
1030 // : unary_operator?
1031 // [ NUMBER | STRING | PERCENTAGE | LENGTH | EMS | EXS | IDENT |
1032 // HEXCOLOR | URL | RGB ]
1034 // operator
1035 // : '/' | ',' | /* empty */
1037 // unary_operator
1038 // : '-' | '+'
1040 // property
1041 // : ident
1043 // das Vorzeichen wird nur fuer numerische Werte (ausser PERCENTAGE)
1044 // beruecksichtigt und wird auf nValue angewendet!
1045 CSS1Expression *CSS1Parser::ParseDeclaration( String& rProperty )
1047 CSS1Expression *pRoot = 0, *pLast = 0;
1049 // property
1050 if( CSS1_IDENT != nToken )
1052 // property fehlt
1053 return pRoot;
1055 rProperty = aToken;
1057 nToken = GetNextToken();
1060 // ':'
1061 if( CSS1_COLON != nToken )
1063 // ':' fehlt
1064 return pRoot;
1066 nToken = GetNextToken();
1068 // term [operator term]*
1069 // hier sind wir sehr lax, was die Syntax angeht, sollte aber kein
1070 // Problem sein
1071 BOOL bDone = FALSE;
1072 sal_Unicode cSign = 0, cOp = 0;
1073 CSS1Expression *pNew = 0;
1075 LOOP_CHECK_DECL
1077 while( !bDone && IsParserWorking() )
1079 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseDeclaration()" )
1081 switch( nToken )
1083 case CSS1_MINUS:
1084 cSign = '-';
1085 break;
1087 case CSS1_PLUS:
1088 cSign = '+';
1089 break;
1091 case CSS1_NUMBER:
1092 case CSS1_LENGTH:
1093 case CSS1_PIXLENGTH:
1094 case CSS1_EMS:
1095 case CSS1_EMX:
1096 if( '-'==cSign )
1097 nValue = -nValue;
1098 case CSS1_STRING:
1099 case CSS1_PERCENTAGE:
1100 case CSS1_IDENT:
1101 case CSS1_URL:
1102 case CSS1_RGB:
1103 case CSS1_HEXCOLOR:
1104 pNew = new CSS1Expression( nToken, aToken, nValue, cOp );
1105 nValue = 0; // sonst landet das auch im naechsten Ident
1106 cSign = 0;
1107 cOp = 0;
1108 break;
1110 case CSS1_SLASH:
1111 cOp = '/';
1112 cSign = 0;
1113 break;
1115 case CSS1_COMMA:
1116 cOp = ',';
1117 cSign = 0;
1118 break;
1120 default:
1121 bDone = TRUE;
1122 break;
1125 // falls ein Expression angelegt wurde, diesen speichern
1126 if( pNew )
1128 DBG_ASSERT( (pRoot!=0) == (pLast!=0),
1129 "Root-Selektor, aber kein Last" );
1130 if( pLast )
1131 pLast->SetNext( pNew );
1132 else
1133 pRoot = pNew;
1135 pLast = pNew;
1136 pNew = 0;
1139 if( !bDone )
1140 nToken = GetNextToken();
1143 if( !pRoot )
1145 // term fehlt
1146 return pRoot;
1149 // prio?
1150 if( CSS1_IMPORTANT_SYM==nToken )
1152 // IMPORTANT_SYM
1153 nToken = GetNextToken();
1156 return pRoot;
1159 /* \f */
1161 CSS1Parser::CSS1Parser()
1165 CSS1Parser::~CSS1Parser()
1169 /* \f */
1171 BOOL CSS1Parser::ParseStyleSheet( const String& rIn )
1173 String aTmp( rIn );
1175 sal_Unicode c;
1176 while( aTmp.Len() &&
1177 ( ' '==(c=aTmp.GetChar(0)) || '\t'==c || '\r'==c || '\n'==c ) )
1178 aTmp.Erase( 0, 1 );
1180 while( aTmp.Len() && ( ' '==(c=aTmp.GetChar( aTmp.Len()-1))
1181 || '\t'==c || '\r'==c || '\n'==c ) )
1182 aTmp.Erase( aTmp.Len()-1 );
1184 // SGML-Kommentare entfernen
1185 if( aTmp.Len() >= 4 &&
1186 aTmp.CompareToAscii("<!--",4) == COMPARE_EQUAL )
1187 aTmp.Erase( 0, 4 );
1189 if( aTmp.Len() >=3 &&
1190 aTmp.Copy(aTmp.Len()-3).CompareToAscii("-->") == COMPARE_EQUAL )
1191 aTmp.Erase( aTmp.Len()-3 );
1193 if( !aTmp.Len() )
1194 return TRUE;
1196 InitRead( aTmp );
1198 ParseStyleSheet();
1200 return TRUE;
1204 BOOL CSS1Parser::ParseStyleOption( const String& rIn )
1206 if( !rIn.Len() )
1207 return TRUE;
1209 InitRead( rIn );
1211 String aProperty;
1212 CSS1Expression *pExpr = ParseDeclaration( aProperty );
1213 if( !pExpr )
1215 return FALSE;
1218 // expression verarbeiten
1219 if( DeclarationParsed( aProperty, pExpr ) )
1220 delete pExpr;
1222 LOOP_CHECK_DECL
1224 // [ ';' declaration ]*
1225 while( CSS1_SEMICOLON==nToken && IsParserWorking() )
1227 LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleOption()" )
1229 nToken = GetNextToken();
1230 if( CSS1_IDENT==nToken )
1232 CSS1Expression *pExp = ParseDeclaration( aProperty );
1233 if( pExp )
1235 // expression verarbeiten
1236 if( DeclarationParsed( aProperty, pExp ) )
1237 delete pExp;
1242 return TRUE;
1245 BOOL CSS1Parser::SelectorParsed( const CSS1Selector * /* pSelector */, BOOL /*bFirst*/ )
1247 // Selektor loeschen
1248 return TRUE;
1251 BOOL CSS1Parser::DeclarationParsed( const String& /*rProperty*/,
1252 const CSS1Expression * /* pExpr */ )
1254 // Deklaration loeschen
1255 return TRUE;
1259 /* \f */
1261 CSS1Selector::~CSS1Selector()
1263 delete pNext;
1266 /* \f */
1268 CSS1Expression::~CSS1Expression()
1270 delete pNext;
1273 BOOL CSS1Expression::GetURL( String& rURL ) const
1275 DBG_ASSERT( CSS1_URL==eType, "CSS1-Ausruck ist keine Farbe URL" );
1277 DBG_ASSERT( aValue.CompareIgnoreCaseToAscii( sCSS1_url, 3 ) ==
1278 COMPARE_EQUAL &&
1279 aValue.Len() > 5 &&
1280 '(' == aValue.GetChar(3) &&
1281 ')' == aValue.GetChar(aValue.Len()-1),
1282 "keine gueltiges URL(...)" );
1284 BOOL bRet = FALSE;
1286 if( aValue.Len() > 5 )
1288 rURL = aValue.Copy( 4, aValue.Len()-5 );
1289 rURL.EraseTrailingChars();
1290 rURL.EraseLeadingChars();
1291 bRet = TRUE;
1294 return bRet;
1297 BOOL CSS1Expression::GetColor( Color &rColor ) const
1299 DBG_ASSERT( CSS1_IDENT==eType || CSS1_RGB==eType ||
1300 CSS1_HEXCOLOR==eType || CSS1_STRING==eType,
1301 "CSS1-Ausruck kann keine Farbe sein" );
1303 BOOL bRet = FALSE;
1304 ULONG nColor = ULONG_MAX;
1306 switch( eType )
1308 case CSS1_RGB:
1310 BYTE aColors[3] = { 0, 0, 0 };
1312 DBG_ASSERT( aValue.CompareIgnoreCaseToAscii( sCSS1_rgb, 3 )
1313 == COMPARE_EQUAL &&
1314 aValue.Len() > 5 &&
1315 '(' == aValue.GetChar( 3 ) &&
1316 ')' == aValue.GetChar( aValue.Len()-1),
1317 "keine gueltiges RGB(...)" );
1319 String aColorStr( aValue.Copy( 4, aValue.Len()-1 ) );
1321 xub_StrLen nPos = 0;
1322 USHORT nCol = 0;
1324 while( nCol < 3 && nPos < aColorStr.Len() )
1326 sal_Unicode c;
1327 while( nPos < aColorStr.Len() &&
1328 ((c=aColorStr.GetChar(nPos)) == ' ' || c == '\t' ||
1329 c == '\n' || c== '\r' ) )
1330 nPos++;
1332 xub_StrLen nEnd = aColorStr.Search( ',', nPos );
1333 String aNumber;
1334 if( STRING_NOTFOUND==nEnd )
1336 aNumber = aColorStr.Copy(nPos);
1337 nPos = aColorStr.Len();
1339 else
1341 aNumber = aColorStr.Copy( nPos, nEnd-nPos );
1342 nPos = nEnd+1;
1345 USHORT nNumber = (USHORT)aNumber.ToInt32();
1346 if( aNumber.Search('%') != STRING_NOTFOUND )
1348 if( nNumber > 100 )
1349 nNumber = 100;
1350 nNumber *= 255;
1351 nNumber /= 100;
1353 else if( nNumber > 255 )
1354 nNumber = 255;
1356 aColors[nCol] = (BYTE)nNumber;
1357 nCol ++;
1360 rColor.SetRed( aColors[0] );
1361 rColor.SetGreen( aColors[1] );
1362 rColor.SetBlue( aColors[2] );
1364 bRet = TRUE; // etwas anderes als eine Farbe kann es nicht sein
1366 break;
1368 case CSS1_IDENT:
1369 case CSS1_STRING:
1371 String aTmp( aValue );
1372 aTmp.ToUpperAscii();
1373 nColor = GetHTMLColor( aTmp );
1374 bRet = nColor != ULONG_MAX;
1376 if( bRet || CSS1_STRING != eType || !aValue.Len() ||
1377 aValue.GetChar( 0 )!='#' )
1378 break;
1380 case CSS1_HEXCOLOR:
1382 // HACK fuer MS-IE: DIe Farbe kann auch in einem String stehen
1383 xub_StrLen nOffset = CSS1_STRING==eType ? 1 : 0;
1384 BOOL bDouble = aValue.Len()-nOffset == 3;
1385 xub_StrLen i = nOffset, nEnd = (bDouble ? 3 : 6) + nOffset;
1387 nColor = 0;
1388 for( ; i<nEnd; i++ )
1390 sal_Unicode c = (i<aValue.Len() ? aValue.GetChar(i)
1391 : '0' );
1392 if( c >= '0' && c <= '9' )
1393 c -= 48;
1394 else if( c >= 'A' && c <= 'F' )
1395 c -= 55;
1396 else if( c >= 'a' && c <= 'f' )
1397 c -= 87;
1398 else
1399 c = 16;
1401 nColor *= 16;
1402 if( c<16 )
1403 nColor += c;
1404 if( bDouble )
1406 nColor *= 16;
1407 if( c<16 )
1408 nColor += c;
1411 // bRet = i==6;
1412 bRet = TRUE;
1414 break;
1415 default:
1420 if( bRet && nColor!=ULONG_MAX )
1422 rColor.SetRed( (BYTE)((nColor & 0x00ff0000UL) >> 16) );
1423 rColor.SetGreen( (BYTE)((nColor & 0x0000ff00UL) >> 8) );
1424 rColor.SetBlue( (BYTE)(nColor & 0x000000ffUL) );
1427 return bRet;