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