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