xref: /trunk/main/sw/source/filter/html/parcss1.cxx (revision efeef26f)
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