xref: /aoo41x/main/svtools/source/svhtml/parhtml.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svtools.hxx"
30 
31 #include <ctype.h>
32 #include <stdio.h>
33 #include <tools/stream.hxx>
34 #include <tools/debug.hxx>
35 #include <tools/color.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <rtl/strbuf.hxx>
38 #ifndef _SVSTDARR_HXX
39 #define _SVSTDARR_ULONGS
40 #include <svl/svstdarr.hxx>
41 #endif
42 
43 #include <tools/tenccvt.hxx>
44 #include <tools/datetime.hxx>
45 #include <svl/inettype.hxx>
46 #include <comphelper/string.hxx>
47 #include <com/sun/star/beans/PropertyAttribute.hpp>
48 #include <com/sun/star/document/XDocumentProperties.hpp>
49 
50 #include <svtools/parhtml.hxx>
51 #include <svtools/htmltokn.h>
52 #include <svtools/htmlkywd.hxx>
53 
54 
55 using namespace ::com::sun::star;
56 
57 
58 const sal_Int32 MAX_LEN( 1024L );
59 //static sal_Unicode sTmpBuffer[ MAX_LEN+1 ];
60 const sal_Int32 MAX_MACRO_LEN( 1024 );
61 
62 const sal_Int32 MAX_ENTITY_LEN( 8L );
63 
64 /*  */
65 
66 // Tabellen zum Umwandeln von Options-Werten in Strings
67 
68 // <INPUT TYPE=xxx>
69 static HTMLOptionEnum __READONLY_DATA aInputTypeOptEnums[] =
70 {
71 	{ OOO_STRING_SVTOOLS_HTML_IT_text,		HTML_IT_TEXT		},
72 	{ OOO_STRING_SVTOOLS_HTML_IT_password,	HTML_IT_PASSWORD	},
73 	{ OOO_STRING_SVTOOLS_HTML_IT_checkbox,	HTML_IT_CHECKBOX	},
74 	{ OOO_STRING_SVTOOLS_HTML_IT_radio,   	HTML_IT_RADIO		},
75 	{ OOO_STRING_SVTOOLS_HTML_IT_range,   	HTML_IT_RANGE		},
76 	{ OOO_STRING_SVTOOLS_HTML_IT_scribble,	HTML_IT_SCRIBBLE	},
77 	{ OOO_STRING_SVTOOLS_HTML_IT_file,    	HTML_IT_FILE		},
78 	{ OOO_STRING_SVTOOLS_HTML_IT_hidden,  	HTML_IT_HIDDEN		},
79 	{ OOO_STRING_SVTOOLS_HTML_IT_submit,  	HTML_IT_SUBMIT		},
80 	{ OOO_STRING_SVTOOLS_HTML_IT_image,   	HTML_IT_IMAGE		},
81 	{ OOO_STRING_SVTOOLS_HTML_IT_reset,   	HTML_IT_RESET		},
82 	{ OOO_STRING_SVTOOLS_HTML_IT_button,   	HTML_IT_BUTTON		},
83 	{ 0,					0					}
84 };
85 
86 // <TABLE FRAME=xxx>
87 static HTMLOptionEnum __READONLY_DATA aTableFrameOptEnums[] =
88 {
89 	{ OOO_STRING_SVTOOLS_HTML_TF_void,	HTML_TF_VOID	},
90 	{ OOO_STRING_SVTOOLS_HTML_TF_above,	HTML_TF_ABOVE	},
91 	{ OOO_STRING_SVTOOLS_HTML_TF_below,	HTML_TF_BELOW	},
92 	{ OOO_STRING_SVTOOLS_HTML_TF_hsides,	HTML_TF_HSIDES	},
93 	{ OOO_STRING_SVTOOLS_HTML_TF_lhs,		HTML_TF_LHS		},
94 	{ OOO_STRING_SVTOOLS_HTML_TF_rhs,		HTML_TF_RHS		},
95 	{ OOO_STRING_SVTOOLS_HTML_TF_vsides,	HTML_TF_VSIDES	},
96 	{ OOO_STRING_SVTOOLS_HTML_TF_box,		HTML_TF_BOX		},
97 	{ OOO_STRING_SVTOOLS_HTML_TF_border,	HTML_TF_BOX		},
98 	{ 0,				0				}
99 };
100 
101 // <TABLE RULES=xxx>
102 static HTMLOptionEnum __READONLY_DATA aTableRulesOptEnums[] =
103 {
104 	{ OOO_STRING_SVTOOLS_HTML_TR_none,	HTML_TR_NONE	},
105 	{ OOO_STRING_SVTOOLS_HTML_TR_groups,	HTML_TR_GROUPS	},
106 	{ OOO_STRING_SVTOOLS_HTML_TR_rows,	HTML_TR_ROWS	},
107 	{ OOO_STRING_SVTOOLS_HTML_TR_cols,	HTML_TR_COLS	},
108 	{ OOO_STRING_SVTOOLS_HTML_TR_all,		HTML_TR_ALL		},
109 	{ 0,				0				}
110 };
111 
112 
113 SV_IMPL_PTRARR(HTMLOptions,HTMLOptionPtr)
114 
115 /*  */
116 
117 sal_uInt16 HTMLOption::GetEnum( const HTMLOptionEnum *pOptEnums, sal_uInt16 nDflt ) const
118 {
119 	sal_uInt16 nValue = nDflt;
120 
121 	while( pOptEnums->pName )
122 		if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) )
123 			break;
124 		else
125 			pOptEnums++;
126 
127 	if( pOptEnums->pName )
128 		nValue = pOptEnums->nValue;
129 
130 	return nValue;
131 }
132 
133 sal_Bool HTMLOption::GetEnum( sal_uInt16 &rEnum, const HTMLOptionEnum *pOptEnums ) const
134 {
135 	while( pOptEnums->pName )
136 	{
137 		if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) )
138 			break;
139 		else
140 			pOptEnums++;
141 	}
142 
143 	const sal_Char *pName = pOptEnums->pName;
144 	if( pName )
145 		rEnum = pOptEnums->nValue;
146 
147 	return (pName != 0);
148 }
149 
150 HTMLOption::HTMLOption( sal_uInt16 nTok, const String& rToken,
151 						const String& rValue )
152 	: aValue(rValue)
153 	, aToken(rToken)
154 	, nToken( nTok )
155 {
156 	DBG_ASSERT( nToken>=HTML_OPTION_START && nToken<HTML_OPTION_END,
157 		"HTMLOption: unbekanntes Token" );
158 }
159 
160 sal_uInt32 HTMLOption::GetNumber() const
161 {
162 	DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START &&
163 				 nToken<HTML_OPTION_NUMBER_END) ||
164 				(nToken>=HTML_OPTION_CONTEXT_START &&
165 				 nToken<HTML_OPTION_CONTEXT_END) ||
166 				nToken==HTML_O_VALUE,
167 		"GetNumber: Option ist nicht numerisch" );
168 	String aTmp( aValue );
169 	aTmp.EraseLeadingChars();
170 	sal_Int32 nTmp = aTmp.ToInt32();
171 	return nTmp >= 0 ? (sal_uInt32)nTmp : 0;
172 }
173 
174 sal_Int32 HTMLOption::GetSNumber() const
175 {
176 	DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START && nToken<HTML_OPTION_NUMBER_END) ||
177 				(nToken>=HTML_OPTION_CONTEXT_START && nToken<HTML_OPTION_CONTEXT_END),
178 		"GetSNumber: Option ist nicht numerisch" );
179 	String aTmp( aValue );
180 	aTmp.EraseLeadingChars();
181 	return aTmp.ToInt32();
182 }
183 
184 void HTMLOption::GetNumbers( SvULongs &rLongs, sal_Bool bSpaceDelim ) const
185 {
186 	if( rLongs.Count() )
187 		rLongs.Remove( 0, rLongs.Count() );
188 
189 	if( bSpaceDelim )
190 	{
191 		// das ist ein sehr stark vereinfachter Scanner. Er sucht einfach
192 		// alle Tiffern aus dem String
193 		sal_Bool bInNum = sal_False;
194 		sal_uLong nNum = 0;
195 		for( xub_StrLen i=0; i<aValue.Len(); i++ )
196 		{
197 			register sal_Unicode c = aValue.GetChar( i );
198 			if( c>='0' && c<='9' )
199 			{
200 				nNum *= 10;
201 				nNum += (c - '0');
202 				bInNum = sal_True;
203 			}
204 			else if( bInNum )
205 			{
206 				rLongs.Insert( nNum, rLongs.Count() );
207 				bInNum = sal_False;
208 				nNum = 0;
209 			}
210 		}
211 		if( bInNum )
212 		{
213 			rLongs.Insert( nNum, rLongs.Count() );
214 		}
215 	}
216 	else
217 	{
218 		// hier wird auf die korrekte Trennung der Zahlen durch ',' geachtet
219 		// und auch mal eine 0 eingefuegt
220 		xub_StrLen nPos = 0;
221 		while( nPos < aValue.Len() )
222 		{
223 			register sal_Unicode c;
224 			while( nPos < aValue.Len() &&
225 				   ((c=aValue.GetChar(nPos)) == ' ' || c == '\t' ||
226 				   c == '\n' || c== '\r' ) )
227 				nPos++;
228 
229 			if( nPos==aValue.Len() )
230 				rLongs.Insert( sal_uLong(0), rLongs.Count() );
231 			else
232 			{
233 				xub_StrLen nEnd = aValue.Search( (sal_Unicode)',', nPos );
234 				if( STRING_NOTFOUND==nEnd )
235 				{
236 					sal_Int32 nTmp = aValue.Copy(nPos).ToInt32();
237 					rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0,
238 								   rLongs.Count() );
239 					nPos = aValue.Len();
240 				}
241 				else
242 				{
243 					sal_Int32 nTmp =
244 						aValue.Copy(nPos,nEnd-nPos).ToInt32();
245 					rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0,
246 								   rLongs.Count() );
247 					nPos = nEnd+1;
248 				}
249 			}
250 		}
251 	}
252 }
253 
254 void HTMLOption::GetColor( Color& rColor ) const
255 {
256 	DBG_ASSERT( (nToken>=HTML_OPTION_COLOR_START && nToken<HTML_OPTION_COLOR_END) || nToken==HTML_O_SIZE,
257 		"GetColor: Option spezifiziert keine Farbe" );
258 
259 	String aTmp( aValue );
260 	aTmp.ToUpperAscii();
261 	sal_uLong nColor = ULONG_MAX;
262 	if( '#'!=aTmp.GetChar( 0 ) )
263 		nColor = GetHTMLColor( aTmp );
264 
265 	if( ULONG_MAX == nColor )
266 	{
267 		nColor = 0;
268 		xub_StrLen nPos = 0;
269 		for( sal_uInt32 i=0; i<6; i++ )
270 		{
271 			// MIB 26.06.97: Wie auch immer Netscape Farbwerte ermittelt,
272 			// maximal drei Zeichen, die kleiner als '0' sind werden
273 			// ignoriert. Bug #40901# stimmt damit. Mal schauen, was sich
274 			// irgendwelche HTML-Autoren noch so einfallen lassen...
275 			register sal_Unicode c = nPos<aTmp.Len() ? aTmp.GetChar( nPos++ )
276 													 : '0';
277 			if( c < '0' )
278 			{
279 				c = nPos<aTmp.Len() ? aTmp.GetChar(nPos++) : '0';
280 				if( c < '0' )
281 					c = nPos<aTmp.Len() ? aTmp.GetChar(nPos++) : '0';
282 			}
283 			nColor *= 16;
284 			if( c >= '0' && c <= '9' )
285 				nColor += (c - 48);
286 			else if( c >= 'A' && c <= 'F' )
287 				nColor += (c - 55);
288 		}
289 	}
290 
291 	rColor.SetRed(   (sal_uInt8)((nColor & 0x00ff0000) >> 16) );
292 	rColor.SetGreen( (sal_uInt8)((nColor & 0x0000ff00) >> 8));
293 	rColor.SetBlue(  (sal_uInt8)(nColor & 0x000000ff) );
294 }
295 
296 HTMLInputType HTMLOption::GetInputType() const
297 {
298 	DBG_ASSERT( nToken==HTML_O_TYPE, "GetInputType: Option nicht TYPE" );
299 	return (HTMLInputType)GetEnum( aInputTypeOptEnums, HTML_IT_TEXT );
300 }
301 
302 HTMLTableFrame HTMLOption::GetTableFrame() const
303 {
304 	DBG_ASSERT( nToken==HTML_O_FRAME, "GetTableFrame: Option nicht FRAME" );
305 	return (HTMLTableFrame)GetEnum( aTableFrameOptEnums, HTML_TF_VOID );
306 }
307 
308 HTMLTableRules HTMLOption::GetTableRules() const
309 {
310 	DBG_ASSERT( nToken==HTML_O_RULES, "GetTableRules: Option nicht RULES" );
311 	return (HTMLTableRules)GetEnum( aTableRulesOptEnums, HTML_TR_NONE );
312 }
313 
314 /*  */
315 
316 HTMLParser::HTMLParser( SvStream& rIn, int bReadNewDoc )
317 	: SvParser( rIn )
318 {
319 	bNewDoc = bReadNewDoc;
320 	bReadListing = bReadXMP = bReadPRE = bReadTextArea =
321 		bReadScript = bReadStyle =
322 		bEndTokenFound = bIsInBody = bReadNextChar =
323 		bReadComment = sal_False;
324 	bIsInHeader = sal_True;
325 	pOptions = new HTMLOptions;
326 }
327 
328 HTMLParser::~HTMLParser()
329 {
330 	if( pOptions && pOptions->Count() )
331 		pOptions->DeleteAndDestroy( 0, pOptions->Count() );
332 	delete pOptions;
333 }
334 
335 SvParserState __EXPORT HTMLParser::CallParser()
336 {
337 	eState = SVPAR_WORKING;
338 	nNextCh = GetNextChar();
339 	SaveState( 0 );
340 
341 	nPre_LinePos = 0;
342 	bPre_IgnoreNewPara = sal_False;
343 
344 	AddRef();
345 	Continue( 0 );
346 	if( SVPAR_PENDING != eState )
347 		ReleaseRef();		// dann brauchen wir den Parser nicht mehr!
348 
349 	return eState;
350 }
351 
352 void HTMLParser::Continue( int nToken )
353 {
354 	if( !nToken )
355 		nToken = GetNextToken();
356 
357 	while( IsParserWorking() )
358 	{
359 		SaveState( nToken );
360 		nToken = FilterToken( nToken );
361 
362 		if( nToken )
363 			NextToken( nToken );
364 
365 		if( IsParserWorking() )
366 			SaveState( 0 );			// bis hierhin abgearbeitet,
367 									// weiter mit neuem Token!
368 		nToken = GetNextToken();
369 	}
370 }
371 
372 int HTMLParser::FilterToken( int nToken )
373 {
374 	switch( nToken )
375 	{
376 	case sal_Unicode(EOF):
377 		nToken = 0;
378 		break;			// nicht verschicken
379 
380 	case HTML_HEAD_OFF:
381 		bIsInBody = sal_True;
382 	case HTML_HEAD_ON:
383 		bIsInHeader = HTML_HEAD_ON == nToken;
384 		break;
385 
386 	case HTML_BODY_ON:
387 	case HTML_FRAMESET_ON:
388 		bIsInHeader = sal_False;
389 		bIsInBody = HTML_BODY_ON == nToken;
390 		break;
391 
392 	case HTML_BODY_OFF:
393 		bIsInBody = bReadPRE = bReadListing = bReadXMP = sal_False;
394 		break;
395 
396 	case HTML_HTML_OFF:
397 		nToken = 0;
398 		bReadPRE = bReadListing = bReadXMP = sal_False;
399 		break;		// HTML_ON wurde auch nicht verschickt !
400 
401 	case HTML_PREFORMTXT_ON:
402 		StartPRE();
403 		break;
404 
405 	case HTML_PREFORMTXT_OFF:
406 		FinishPRE();
407 		break;
408 
409 	case HTML_LISTING_ON:
410 		StartListing();
411 		break;
412 
413 	case HTML_LISTING_OFF:
414 		FinishListing();
415 		break;
416 
417 	case HTML_XMP_ON:
418 		StartXMP();
419 		break;
420 
421 	case HTML_XMP_OFF:
422 		FinishXMP();
423 		break;
424 
425 	default:
426 		if( bReadPRE )
427 			nToken = FilterPRE( nToken );
428 		else if( bReadListing )
429 			nToken = FilterListing( nToken );
430 		else if( bReadXMP )
431 			nToken = FilterXMP( nToken );
432 
433 		break;
434 	}
435 
436 	return nToken;
437 }
438 
439 #define HTML_ISDIGIT( c ) (c >= '0' && c <= '9')
440 #define HTML_ISALPHA( c ) ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') )
441 #define HTML_ISALNUM( c ) ( HTML_ISALPHA(c) || HTML_ISDIGIT(c) )
442 #define HTML_ISSPACE( c ) ( ' ' == c || (c >= 0x09 && c <= 0x0d) )
443 #define HTML_ISPRINTABLE( c ) ( c >= 32 && c != 127)
444 // --> OD 2006-07-26 #138464#
445 #define HTML_ISHEXDIGIT( c ) ( HTML_ISDIGIT(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') )
446 // <--
447 
448 int HTMLParser::ScanText( const sal_Unicode cBreak )
449 {
450 	::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
451 	int bWeiter = sal_True;
452 	int bEqSignFound = sal_False;
453 	sal_Unicode cQuote = 0U;
454 
455 	while( bWeiter && IsParserWorking() )
456 	{
457 		int bNextCh = sal_True;
458 		switch( nNextCh )
459 		{
460 		case '&':
461 			bEqSignFound = sal_False;
462 			if( bReadXMP )
463 				sTmpBuffer.append( (sal_Unicode)'&' );
464 			else
465 			{
466 				sal_uLong nStreamPos = rInput.Tell();
467 				sal_uLong nLinePos = GetLinePos();
468 
469 				sal_Unicode cChar = 0U;
470 				if( '#' == (nNextCh = GetNextChar()) )
471 				{
472 					nNextCh = GetNextChar();
473                     // --> OD 2006-07-26 #138464#
474                     // consider hexadecimal digits
475                     const sal_Bool bIsHex( 'x' == nNextCh );
476                     const sal_Bool bIsDecOrHex( bIsHex || HTML_ISDIGIT(nNextCh) );
477                     if ( bIsDecOrHex )
478 					{
479                         if ( bIsHex )
480                         {
481                             nNextCh = GetNextChar();
482                             while ( HTML_ISHEXDIGIT(nNextCh) )
483                             {
484                                 cChar = cChar * 16U +
485                                         ( nNextCh <= '9'
486                                           ? sal_Unicode( nNextCh - '0' )
487                                           : ( nNextCh <= 'F'
488                                               ? sal_Unicode( nNextCh - 'A' + 10 )
489                                               : sal_Unicode( nNextCh - 'a' + 10 ) ) );
490                                 nNextCh = GetNextChar();
491                             }
492                         }
493                         else
494                         {
495                             do
496                             {
497                                 cChar = cChar * 10U + sal_Unicode( nNextCh - '0');
498                                 nNextCh = GetNextChar();
499                             }
500                             while( HTML_ISDIGIT(nNextCh) );
501                         }
502 
503 						if( RTL_TEXTENCODING_DONTKNOW != eSrcEnc &&
504 							RTL_TEXTENCODING_UCS2 != eSrcEnc &&
505 							RTL_TEXTENCODING_UTF8 != eSrcEnc &&
506 						 	cChar < 256 )
507 						{
508 						 	sal_Unicode cOrig = cChar;
509 							cChar = ByteString::ConvertToUnicode(
510 											(sal_Char)cChar, eSrcEnc );
511 							if( 0U == cChar )
512 							{
513 								// #73398#: If the character could not be
514 								// converted, because a conversion is not
515 								// available, do no conversion at all.
516 								cChar = cOrig;
517 							}
518 						}
519 					}
520                     // <--
521 					else
522 						nNextCh = 0U;
523 				}
524 				else if( HTML_ISALPHA( nNextCh ) )
525 				{
526 					::rtl::OUStringBuffer sEntityBuffer( MAX_ENTITY_LEN );
527 					xub_StrLen nPos = 0L;
528 					do
529 					{
530 						sEntityBuffer.append( nNextCh );
531 						nPos++;
532 						nNextCh = GetNextChar();
533 					}
534 					while( nPos < MAX_ENTITY_LEN && HTML_ISALNUM( nNextCh ) &&
535 						   !rInput.IsEof() );
536 
537 					if( IsParserWorking() && !rInput.IsEof() )
538 					{
539 						String sEntity( sEntityBuffer.getStr(), nPos );
540 						cChar = GetHTMLCharName( sEntity );
541 
542 						// nicht gefunden ( == 0 ), dann Klartext
543 						// oder ein Zeichen das als Attribut eingefuegt
544 						// wird
545 						if( 0U == cChar && ';' != nNextCh )
546 						{
547 							DBG_ASSERT( rInput.Tell() - nStreamPos ==
548 										(sal_uLong)(nPos+1L)*GetCharSize(),
549 										"UTF-8 geht hier schief" );
550 							for( xub_StrLen i=nPos-1L; i>1L; i-- )
551 							{
552 								nNextCh = sEntityBuffer[i];
553 								sEntityBuffer.setLength( i );
554 								sEntity.Assign( sEntityBuffer.getStr(), i );
555  								cChar = GetHTMLCharName( sEntity );
556 								if( cChar )
557 								{
558 									rInput.SeekRel( -(long)
559 											((nPos-i)*GetCharSize()) );
560 									nlLinePos -= sal_uInt32(nPos-i);
561 									nPos = i;
562 									ClearTxtConvContext();
563 									break;
564 								}
565 							}
566 						}
567 
568 						if( !cChar )		// unbekanntes Zeichen?
569 						{
570 							// dann im Stream zurueck, das '&' als Zeichen
571 							// einfuegen und mit dem nachfolgenden Zeichen
572 							// wieder aufsetzen
573 							sTmpBuffer.append( (sal_Unicode)'&' );
574 
575 //							rInput.SeekRel( -(long)(++nPos*GetCharSize()) );
576 //							nlLinePos -= nPos;
577 							DBG_ASSERT( rInput.Tell()-nStreamPos ==
578 										(sal_uLong)(nPos+1)*GetCharSize(),
579 										"Falsche Stream-Position" );
580 							DBG_ASSERT( nlLinePos-nLinePos ==
581 										(sal_uLong)(nPos+1),
582 										"Falsche Zeilen-Position" );
583 							rInput.Seek( nStreamPos );
584 							nlLinePos = nLinePos;
585 							ClearTxtConvContext();
586 							break;
587 						}
588 
589 						// 1 == Non Breaking Space
590 						// 2 == SoftHyphen
591 
592 						if( cChar < 3U )
593 						{
594 							if( '>' == cBreak )
595 							{
596 								// Wenn der Inhalt eines Tags gelesen wird,
597 								// muessen wir ein Space bzw. - daraus machen
598 								switch( cChar )
599 								{
600 								case 1U: cChar = ' '; break;
601 								case 2U: cChar = '-'; break;
602 								default:
603 									DBG_ASSERT( cChar==1U,
604 							"\0x00 sollte doch schon laengt abgefangen sein!" );
605 									break;
606 								}
607 							}
608 							else
609 							{
610 								// Wenn kein Tag gescannt wird, enstprechendes
611 								// Token zurueckgeben
612 								aToken +=
613 									String( sTmpBuffer.makeStringAndClear() );
614 								if( cChar )
615 								{
616 									if( aToken.Len() )
617 									{
618 										// mit dem Zeichen wieder aufsetzen
619 										nNextCh = '&';
620 //										rInput.SeekRel( -(long)(++nPos*GetCharSize()) );
621 //										nlLinePos -= nPos;
622 										DBG_ASSERT( rInput.Tell()-nStreamPos ==
623 													(sal_uLong)(nPos+1)*GetCharSize(),
624 													"Falsche Stream-Position" );
625 										DBG_ASSERT( nlLinePos-nLinePos ==
626 													(sal_uLong)(nPos+1),
627 													"Falsche Zeilen-Position" );
628 										rInput.Seek( nStreamPos );
629 										nlLinePos = nLinePos;
630 										ClearTxtConvContext();
631 										return HTML_TEXTTOKEN;
632 									}
633 
634 									// Hack: _GetNextChar soll nicht das
635 									// naechste Zeichen lesen
636 									if( ';' != nNextCh )
637 										aToken += ' ';
638 									if( 1U == cChar )
639 										return HTML_NONBREAKSPACE;
640 									if( 2U == cChar )
641 										return HTML_SOFTHYPH;
642 								}
643 								aToken += (sal_Unicode)'&';
644 								aToken +=
645 									String(sEntityBuffer.makeStringAndClear());
646 								break;
647 							}
648 						}
649 					}
650 					else
651 						nNextCh = 0U;
652 				}
653 				// MIB 03/02/2000: &{...};-JavaScript-Macros are not
654 				// supported any longer.
655 				else if( IsParserWorking() )
656 				{
657 					sTmpBuffer.append( (sal_Unicode)'&' );
658 					bNextCh = sal_False;
659 					break;
660 				}
661 
662 				bNextCh = (';' == nNextCh);
663 				if( cBreak=='>' && (cChar=='\\' || cChar=='\'' ||
664 									cChar=='\"' || cChar==' ') )
665 				{
666 					// ' und " mussen innerhalb von Tags mit einem
667 					// gekennzeichnet werden, um sie von ' und " als Klammern
668 					// um Optionen zu unterscheiden. Logischerweise muss
669 					// deshalb auch ein \ gekeenzeichnet werden. Ausserdem
670 					// schuetzen wir ein Space, weil es kein Trennzeichen
671 					// zwischen Optionen ist.
672 					sTmpBuffer.append( (sal_Unicode)'\\' );
673 					if( MAX_LEN == sTmpBuffer.getLength() )
674 						aToken += String(sTmpBuffer.makeStringAndClear());
675 				}
676 				if( IsParserWorking() )
677 				{
678 					if( cChar )
679 						sTmpBuffer.append( cChar );
680 				}
681 				else if( SVPAR_PENDING==eState && '>'!=cBreak )
682 				{
683 					// Mit dem '&' Zeichen wieder aufsetzen, der Rest
684 					// wird als Texttoken zurueckgegeben.
685 					if( aToken.Len() || sTmpBuffer.getLength() )
686 					{
687 						// Der bisherige Text wird von _GetNextChar()
688 						// zurueckgegeben und beim naechsten Aufruf wird
689 						// ein neues Zeichen gelesen. Also muessen wir uns
690 						// noch vor das & stellen.
691 						nNextCh = 0U;
692 						rInput.Seek( nStreamPos-(sal_uInt32)GetCharSize() );
693 						nlLinePos = nLinePos-1;
694 						ClearTxtConvContext();
695 						bReadNextChar = sal_True;
696 					}
697 					bNextCh = sal_False;
698 				}
699 			}
700 			break;
701 		case '=':
702 			if( '>'==cBreak && !cQuote )
703 				bEqSignFound = sal_True;
704 			sTmpBuffer.append( nNextCh );
705 			break;
706 
707 		case '\\':
708 			if( '>'==cBreak )
709 			{
710 				// Innerhalb von Tags kennzeichnen
711 				sTmpBuffer.append( (sal_Unicode)'\\' );
712 				if( MAX_LEN == sTmpBuffer.getLength() )
713 					aToken += String(sTmpBuffer.makeStringAndClear());
714 			}
715 			sTmpBuffer.append( (sal_Unicode)'\\' );
716 			break;
717 
718 		case '\"':
719 		case '\'':
720 			if( '>'==cBreak )
721 			{
722 				if( bEqSignFound )
723 					cQuote = nNextCh;
724 				else if( cQuote && (cQuote==nNextCh ) )
725 					cQuote = 0U;
726 			}
727 			sTmpBuffer.append( nNextCh );
728 			bEqSignFound = sal_False;
729 			break;
730 
731 		case sal_Unicode(EOF):
732 			if( rInput.IsEof() )
733 			{
734 // MIB 20.11.98: Das macht hier keinen Sinn, oder doch: Zumindest wird
735 // abc&auml;<EOF> nicht angezeigt, also lassen wir das in Zukunft.
736 //				if( '>' != cBreak )
737 //					eState = SVPAR_ACCEPTED;
738 				bWeiter = sal_False;
739 			}
740 			else
741 			{
742 				sTmpBuffer.append( nNextCh );
743 			}
744 			break;
745 
746 		case '<':
747 			bEqSignFound = sal_False;
748 			if( '>'==cBreak )
749 				sTmpBuffer.append( nNextCh );
750 			else
751 				bWeiter = sal_False;		// Abbrechen, String zusammen
752 			break;
753 
754 		case '\f':
755 			if( '>' == cBreak )
756 			{
757 				// Beim Scannen von Optionen wie ein Space behandeln
758 				sTmpBuffer.append( (sal_Unicode)' ' );
759 			}
760 			else
761 			{
762 				// sonst wird es ein eigenes Token
763 				bWeiter = sal_False;
764 			}
765 			break;
766 
767 		case '\r':
768 		case '\n':
769 			if( '>'==cBreak )
770 			{
771 				// #26979# cr/lf in Tag wird in _GetNextToken() behandeln
772 				sTmpBuffer.append( nNextCh );
773 				break;
774 			}
775 			else if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
776 			{
777 				bWeiter = sal_False;
778 				break;
779 			}
780 			// Bug 18984: CR-LF -> Blank
781 			// 		Folge von CR/LF/BLANK/TAB nur in ein Blank wandeln
782 			// kein break!!
783 		case '\t':
784 			if( '\t'==nNextCh && bReadPRE && '>'!=cBreak )
785 			{
786 				// In <PRE>: Tabs nach oben durchreichen
787 				bWeiter = sal_False;
788 				break;
789 			}
790 			// kein break
791 		case '\x0b':
792 			if( '\x0b'==nNextCh && (bReadPRE || bReadXMP ||bReadListing) &&
793 				'>'!=cBreak )
794 			{
795 				break;
796 			}
797 			nNextCh = ' ';
798 			// kein break;
799 		case ' ':
800 			sTmpBuffer.append( nNextCh );
801 			if( '>'!=cBreak && (!bReadListing && !bReadXMP &&
802 								!bReadPRE && !bReadTextArea) )
803 			{
804 				// alle Folgen von Blanks/Tabs/CR/LF zu einem Blank umwandeln
805 				do {
806 					if( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
807 						rInput.IsEof() )
808 					{
809 						if( aToken.Len() || sTmpBuffer.getLength() > 1L )
810 						{
811 							// ausser den Blanks wurde noch etwas geselen
812 							aToken += String(sTmpBuffer.makeStringAndClear());
813 							return HTML_TEXTTOKEN;
814 						}
815 						else
816 							// nur Blanks gelesen: dann darf kein Text
817 							// mehr zurueckgegeben werden und _GetNextToken
818 							// muss auf EOF laufen
819 							return 0;
820 					}
821 				} while ( ' ' == nNextCh || '\t' == nNextCh ||
822 						  '\r' == nNextCh || '\n' == nNextCh ||
823 						  '\x0b' == nNextCh );
824 				bNextCh = sal_False;
825 			}
826 			break;
827 
828 		default:
829 			bEqSignFound = sal_False;
830 			if( (nNextCh==cBreak && !cQuote) ||
831 				(sal_uLong(aToken.Len()) + MAX_LEN) > sal_uLong(STRING_MAXLEN & ~1 ))
832 				bWeiter = sal_False;
833 			else
834 			{
835 				do {
836 					// alle anderen Zeichen kommen in den Text
837 					sTmpBuffer.append( nNextCh );
838 					if( MAX_LEN == sTmpBuffer.getLength() )
839 					{
840 						aToken += String(sTmpBuffer.makeStringAndClear());
841 						if( (sal_uLong(aToken.Len()) + MAX_LEN) >
842 								sal_uLong(STRING_MAXLEN & ~1 ) )
843 						{
844 							nNextCh = GetNextChar();
845 							return HTML_TEXTTOKEN;
846 						}
847 					}
848 					if( ( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
849 						  rInput.IsEof() ) ||
850 						!IsParserWorking() )
851 					{
852 						if( sTmpBuffer.getLength() )
853 							aToken += String(sTmpBuffer.makeStringAndClear());
854 						return HTML_TEXTTOKEN;
855 					}
856 				} while( HTML_ISALPHA( nNextCh ) || HTML_ISDIGIT( nNextCh ) );
857 				bNextCh = sal_False;
858 			}
859 		}
860 
861 		if( MAX_LEN == sTmpBuffer.getLength() )
862 			aToken += String(sTmpBuffer.makeStringAndClear());
863 
864 		if( bWeiter && bNextCh )
865 			nNextCh = GetNextChar();
866 	}
867 
868 	if( sTmpBuffer.getLength() )
869 		aToken += String(sTmpBuffer.makeStringAndClear());
870 
871 	return HTML_TEXTTOKEN;
872 }
873 
874 int HTMLParser::_GetNextRawToken()
875 {
876 	::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
877 
878 	if( bEndTokenFound )
879 	{
880 		// beim letzten Aufruf haben wir das End-Token bereits gefunden,
881 		// deshalb muessen wir es nicht noch einmal suchen
882 		bReadScript = sal_False;
883 		bReadStyle = sal_False;
884 		aEndToken.Erase();
885 		bEndTokenFound = sal_False;
886 
887 		return 0;
888 	}
889 
890 	// per default geben wir HTML_RAWDATA zurueck
891 	int bWeiter = sal_True;
892 	int nToken = HTML_RAWDATA;
893 	SaveState( 0 );
894 	while( bWeiter && IsParserWorking() )
895 	{
896 		int bNextCh = sal_True;
897 		switch( nNextCh )
898 		{
899 		case '<':
900 			{
901 				// Vielleicht haben wir das Ende erreicht
902 
903 				// das bisher gelesene erstmal retten
904 				aToken += String(sTmpBuffer.makeStringAndClear());
905 
906 				// und die Position im Stream merken
907 				sal_uLong nStreamPos = rInput.Tell();
908 				sal_uLong nLineNr = GetLineNr();
909 				sal_uLong nLinePos = GetLinePos();
910 
911 				// Start eines End-Token?
912 				int bOffState = sal_False;
913 				if( '/' == (nNextCh = GetNextChar()) )
914 				{
915 					bOffState = sal_True;
916 					nNextCh = GetNextChar();
917 				}
918 				else if( '!' == nNextCh )
919 				{
920 					sTmpBuffer.append( nNextCh );
921 					nNextCh = GetNextChar();
922 				}
923 
924 				// jetzt die Buchstaben danach lesen
925 				while( (HTML_ISALPHA(nNextCh) || '-'==nNextCh) &&
926 					   IsParserWorking() && sTmpBuffer.getLength() < MAX_LEN )
927 				{
928 					sTmpBuffer.append( nNextCh );
929 					nNextCh = GetNextChar();
930 				}
931 
932 				String aTok( sTmpBuffer.getStr(),
933 							 sal::static_int_cast< xub_StrLen >(
934                                  sTmpBuffer.getLength()) );
935 				aTok.ToUpperAscii();
936 				sal_Bool bDone = sal_False;
937 				if( bReadScript || aEndToken.Len() )
938 				{
939 					if( !bReadComment )
940 					{
941 						if( aTok.CompareToAscii( OOO_STRING_SVTOOLS_HTML_comment, 3 )
942 								== COMPARE_EQUAL )
943 						{
944 							bReadComment = sal_True;
945 						}
946 						else
947 						{
948 							// ein Script muss mit "</SCRIPT>" aufhoehren, wobei
949 							// wir es mit dem ">" aus sicherheitsgruenden
950 							// erstmal nicht so genau nehmen
951 							bDone = bOffState && // '>'==nNextCh &&
952 							COMPARE_EQUAL == ( bReadScript
953 								? aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_script)
954 								: aTok.CompareTo(aEndToken) );
955 						}
956 					}
957 					if( bReadComment && '>'==nNextCh && aTok.Len() >= 2 &&
958 						aTok.Copy( aTok.Len()-2 ).EqualsAscii( "--" ) )
959 					{
960 						// hier ist ein Kommentar der Art <!-----> zuende
961 						bReadComment = sal_False;
962 					}
963 				}
964 				else
965 				{
966 					// ein Style-Sheet kann mit </STYLE>, </HEAD> oder
967 					// <BODY> aughoehren
968 					if( bOffState )
969 						bDone = aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_style)
970 									== COMPARE_EQUAL ||
971 								aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_head)
972 									== COMPARE_EQUAL;
973 					else
974 						bDone =
975 							aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_body) == COMPARE_EQUAL;
976 				}
977 
978 				if( bDone )
979 				{
980 					// das war's, jetzt muessen wir gegebenenfalls den
981 					// bisher gelesenen String zurueckgeben und dnach normal
982 					// weitermachen
983 
984 					bWeiter = sal_False;
985 
986 					// nToken==0 heisst, dass _GetNextToken gleich weiterliest
987 					if( !aToken.Len() && (bReadStyle || bReadScript) )
988 					{
989 						// wir koennen sofort die Umgebung beeden und
990 						// das End-Token parsen
991 						bReadScript = sal_False;
992 						bReadStyle = sal_False;
993 						aEndToken.Erase();
994 						nToken = 0;
995 					}
996 					else
997 					{
998 						// wir muessen bReadScript/bReadStyle noch am
999 						// Leben lassen und koennen erst beim naechsten
1000 						// mal das End-Token Parsen
1001 						bEndTokenFound = sal_True;
1002 					}
1003 
1004 					// jetzt fahren wir im Stream auf das '<' zurueck
1005 					rInput.Seek( nStreamPos );
1006 					SetLineNr( nLineNr );
1007 					SetLinePos( nLinePos );
1008 					ClearTxtConvContext();
1009 					nNextCh = '<';
1010 
1011 					// den String wollen wir nicht an das Token haengen
1012 					sTmpBuffer.setLength( 0L );
1013 				}
1014 				else
1015 				{
1016 					// "</" merken, alles andere steht noch im buffer
1017 					aToken += (sal_Unicode)'<';
1018 					if( bOffState )
1019 						aToken += (sal_Unicode)'/';
1020 
1021 					bNextCh = sal_False;
1022 				}
1023 			}
1024 			break;
1025 		case '-':
1026 			sTmpBuffer.append( nNextCh );
1027 			if( bReadComment )
1028 			{
1029 				sal_Bool bTwoMinus = sal_False;
1030 				nNextCh = GetNextChar();
1031 				while( '-' == nNextCh && IsParserWorking() )
1032 				{
1033 					bTwoMinus = sal_True;
1034 
1035 					if( MAX_LEN == sTmpBuffer.getLength() )
1036 						aToken += String(sTmpBuffer.makeStringAndClear());
1037 					sTmpBuffer.append( nNextCh );
1038 					nNextCh = GetNextChar();
1039 				}
1040 
1041 				if( '>' == nNextCh && IsParserWorking() && bTwoMinus )
1042 					bReadComment = sal_False;
1043 
1044 				bNextCh = sal_False;
1045 			}
1046 			break;
1047 
1048 		case '\r':
1049 			// \r\n? beendet das aktuelle Text-Token (auch wenn es leer ist)
1050 			nNextCh = GetNextChar();
1051 			if( nNextCh=='\n' )
1052 				nNextCh = GetNextChar();
1053 			bWeiter = sal_False;
1054 			break;
1055 		case '\n':
1056 			// \n beendet das aktuelle Text-Token (auch wenn es leer ist)
1057 			nNextCh = GetNextChar();
1058 			bWeiter = sal_False;
1059 			break;
1060 		case sal_Unicode(EOF):
1061 			// eof beendet das aktuelle Text-Token und tut so, als ob
1062 			// ein End-Token gelesen wurde
1063 			if( rInput.IsEof() )
1064 			{
1065 				bWeiter = sal_False;
1066 				if( aToken.Len() || sTmpBuffer.getLength() )
1067 				{
1068 					bEndTokenFound = sal_True;
1069 				}
1070 				else
1071 				{
1072 					bReadScript = sal_False;
1073 					bReadStyle = sal_False;
1074 					aEndToken.Erase();
1075 					nToken = 0;
1076 				}
1077 				break;
1078 			}
1079 			// kein break
1080 		default:
1081 			// alle anderen Zeichen landen im Buffer
1082 			sTmpBuffer.append( nNextCh );
1083 			break;
1084 		}
1085 
1086 		if( (!bWeiter && sTmpBuffer.getLength() > 0L) ||
1087 			MAX_LEN == sTmpBuffer.getLength() )
1088 			aToken += String(sTmpBuffer.makeStringAndClear());
1089 
1090 		if( bWeiter && bNextCh )
1091 			nNextCh = GetNextChar();
1092 	}
1093 
1094 	if( IsParserWorking() )
1095 		SaveState( 0 );
1096 	else
1097 		nToken = 0;
1098 
1099 	return nToken;
1100 }
1101 
1102 // scanne das naechste Token,
1103 int __EXPORT HTMLParser::_GetNextToken()
1104 {
1105 	int nRet = 0;
1106 	sSaveToken.Erase();
1107 
1108 	// die Optionen loeschen
1109 	if( pOptions->Count() )
1110 		pOptions->DeleteAndDestroy( 0, pOptions->Count() );
1111 
1112 	if( !IsParserWorking() )		// wenn schon Fehler, dann nicht weiter!
1113 		return 0;
1114 
1115 	sal_Bool bReadNextCharSave = bReadNextChar;
1116 	if( bReadNextChar )
1117 	{
1118 		DBG_ASSERT( !bEndTokenFound,
1119 					"</SCRIPT> gelesen und trotzdem noch ein Zeichen lesen?" );
1120 		nNextCh = GetNextChar();
1121 		if( !IsParserWorking() )		// wenn schon Fehler, dann nicht weiter!
1122 			return 0;
1123 		bReadNextChar = sal_False;
1124 	}
1125 
1126 	if( bReadScript || bReadStyle || aEndToken.Len() )
1127 	{
1128 		nRet = _GetNextRawToken();
1129 		if( nRet || !IsParserWorking() )
1130 			return nRet;
1131 	}
1132 
1133 	do {
1134 		int bNextCh = sal_True;
1135 		switch( nNextCh )
1136 		{
1137 		case '<':
1138 			{
1139 				sal_uLong nStreamPos = rInput.Tell();
1140 				sal_uLong nLineNr = GetLineNr();
1141 				sal_uLong nLinePos = GetLinePos();
1142 
1143 				int bOffState = sal_False;
1144 				if( '/' == (nNextCh = GetNextChar()) )
1145 				{
1146 					bOffState = sal_True;
1147 					nNextCh = GetNextChar();
1148 				}
1149 				if( HTML_ISALPHA( nNextCh ) || '!'==nNextCh ) // fix #26984#
1150 				{
1151 					::rtl::OUStringBuffer sTmpBuffer;
1152 					do {
1153 						sTmpBuffer.append( nNextCh );
1154 						if( MAX_LEN == sTmpBuffer.getLength() )
1155 							aToken += String(sTmpBuffer.makeStringAndClear());
1156 						nNextCh = GetNextChar();
1157 					} while( '>' != nNextCh && !HTML_ISSPACE( nNextCh ) &&
1158 							 IsParserWorking() && !rInput.IsEof() );
1159 
1160 					if( sTmpBuffer.getLength() )
1161 						aToken += String(sTmpBuffer.makeStringAndClear());
1162 
1163 					// Blanks ueberlesen
1164 					while( HTML_ISSPACE( nNextCh ) && IsParserWorking() )
1165 						nNextCh = GetNextChar();
1166 
1167 					if( !IsParserWorking() )
1168 					{
1169 						if( SVPAR_PENDING == eState )
1170 							bReadNextChar = bReadNextCharSave;
1171 						break;
1172 					}
1173 
1174 					// suche das Token in der Tabelle:
1175 					sSaveToken = aToken;
1176 					aToken.ToUpperAscii();
1177 					if( 0 == (nRet = GetHTMLToken( aToken )) )
1178 						// Unknown Control
1179 						nRet = HTML_UNKNOWNCONTROL_ON;
1180 
1181 					// Wenn es ein Token zum ausschalten ist ...
1182 					if( bOffState )
1183 					{
1184 						 if( HTML_TOKEN_ONOFF & nRet )
1185 						 {
1186 							// und es ein Off-Token gibt, das daraus machen
1187 							++nRet;
1188 						 }
1189 						 else if( HTML_LINEBREAK!=nRet )
1190 						 {
1191 							// und es kein Off-Token gibt, ein unbekanntes
1192 							// Token daraus machen (ausser </BR>, das wird
1193 							// wie <BR> behandelt
1194 							nRet = HTML_UNKNOWNCONTROL_OFF;
1195 						 }
1196 					}
1197 
1198 					if( nRet == HTML_COMMENT )
1199 					{
1200 						// fix: sSaveToken wegen Gross-/Kleinschreibung
1201 						// als Anfang des Kommentars benutzen und ein
1202 						// Space anhaengen.
1203 						aToken = sSaveToken;
1204 						if( '>'!=nNextCh )
1205 							aToken += (sal_Unicode)' ';
1206 						sal_uLong nCStreamPos = 0;
1207 						sal_uLong nCLineNr = 0;
1208 						sal_uLong nCLinePos = 0;
1209 						xub_StrLen nCStrLen = 0;
1210 
1211 						sal_Bool bDone = sal_False;
1212 						// bis zum schliessenden --> lesen. wenn keins gefunden
1213 						// wurde beim der ersten > wieder aufsetzen
1214 						while( !bDone && !rInput.IsEof() && IsParserWorking() )
1215 						{
1216 							if( '>'==nNextCh )
1217 							{
1218 								if( !nCStreamPos )
1219 								{
1220 									nCStreamPos = rInput.Tell();
1221 									nCStrLen = aToken.Len();
1222 									nCLineNr = GetLineNr();
1223 									nCLinePos = GetLinePos();
1224 								}
1225 								bDone = aToken.Len() >= 2 &&
1226 										aToken.Copy(aToken.Len()-2,2).
1227 														EqualsAscii( "--" );
1228 								if( !bDone )
1229 								aToken += nNextCh;
1230 							}
1231 							else
1232 								aToken += nNextCh;
1233 							if( !bDone )
1234 								nNextCh = GetNextChar();
1235 						}
1236 						if( !bDone && IsParserWorking() && nCStreamPos )
1237 						{
1238 							rInput.Seek( nCStreamPos );
1239 							SetLineNr( nCLineNr );
1240 							SetLinePos( nCLinePos );
1241 							ClearTxtConvContext();
1242 							aToken.Erase( nCStrLen );
1243 							nNextCh = '>';
1244 						}
1245 					}
1246 					else
1247 					{
1248 						// den TokenString koennen wir jetzt verwerfen
1249 						aToken.Erase();
1250 					}
1251 
1252 					// dann lesen wir mal alles bis zur schliessenden '>'
1253 					if( '>' != nNextCh && IsParserWorking() )
1254 					{
1255 						ScanText( '>' );
1256 						if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
1257 						{
1258 							// zurueck hinter die < gehen  und dort neu
1259 							// aufsetzen, das < als Text zurueckgeben
1260 							rInput.Seek( nStreamPos );
1261 							SetLineNr( nLineNr );
1262 							SetLinePos( nLinePos );
1263 							ClearTxtConvContext();
1264 
1265 							aToken = '<';
1266 							nRet = HTML_TEXTTOKEN;
1267 							nNextCh = GetNextChar();
1268 							bNextCh = sal_False;
1269 							break;
1270 						}
1271 					}
1272 					if( SVPAR_PENDING == eState )
1273 						bReadNextChar = bReadNextCharSave;
1274 				}
1275 				else
1276 				{
1277 					if( bOffState )
1278 					{
1279 						// einfach alles wegschmeissen
1280 						ScanText( '>' );
1281 						if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
1282 						{
1283 							// zurueck hinter die < gehen  und dort neu
1284 							// aufsetzen, das < als Text zurueckgeben
1285 							rInput.Seek( nStreamPos );
1286 							SetLineNr( nLineNr );
1287 							SetLinePos( nLinePos );
1288 							ClearTxtConvContext();
1289 
1290 							aToken = '<';
1291 							nRet = HTML_TEXTTOKEN;
1292 							nNextCh = GetNextChar();
1293 							bNextCh = sal_False;
1294 							break;
1295 						}
1296 						if( SVPAR_PENDING == eState )
1297 							bReadNextChar = bReadNextCharSave;
1298 						aToken.Erase();
1299 					}
1300 					else if( '%' == nNextCh )
1301 					{
1302 						nRet = HTML_UNKNOWNCONTROL_ON;
1303 
1304 						sal_uLong nCStreamPos = rInput.Tell();
1305 						sal_uLong nCLineNr = GetLineNr(), nCLinePos = GetLinePos();
1306 
1307 						sal_Bool bDone = sal_False;
1308 						// bis zum schliessenden %> lesen. wenn keins gefunden
1309 						// wurde beim der ersten > wieder aufsetzen
1310 						while( !bDone && !rInput.IsEof() && IsParserWorking() )
1311 						{
1312 							bDone = '>'==nNextCh && aToken.Len() >= 1 &&
1313 									'%' == aToken.GetChar( aToken.Len()-1 );
1314 							if( !bDone )
1315 							{
1316 								aToken += nNextCh;
1317 								nNextCh = GetNextChar();
1318 							}
1319 						}
1320 						if( !bDone && IsParserWorking() )
1321 						{
1322 							rInput.Seek( nCStreamPos );
1323 							SetLineNr( nCLineNr );
1324 							SetLinePos( nCLinePos );
1325 							ClearTxtConvContext();
1326 							aToken.AssignAscii( "<%", 2 );
1327 							nRet = HTML_TEXTTOKEN;
1328 							break;
1329 						}
1330 						if( IsParserWorking() )
1331 						{
1332 							sSaveToken = aToken;
1333 							aToken.Erase();
1334 						}
1335 					}
1336 					else
1337 					{
1338 						aToken = '<';
1339 						nRet = HTML_TEXTTOKEN;
1340 						bNextCh = sal_False;
1341 						break;
1342 					}
1343 				}
1344 
1345 				if( IsParserWorking() )
1346 				{
1347 					bNextCh = '>' == nNextCh;
1348 					switch( nRet )
1349 					{
1350 					case HTML_TEXTAREA_ON:
1351 						bReadTextArea = sal_True;
1352 						break;
1353 					case HTML_TEXTAREA_OFF:
1354 						bReadTextArea = sal_False;
1355 						break;
1356 					case HTML_SCRIPT_ON:
1357 						if( !bReadTextArea )
1358 							bReadScript = sal_True;
1359 						break;
1360 					case HTML_SCRIPT_OFF:
1361 						if( !bReadTextArea )
1362 						{
1363 							bReadScript = sal_False;
1364 							// JavaScript kann den Stream veraendern
1365 							// also muss das letzte Zeichen nochmals
1366 							// gelesen werden
1367 							bReadNextChar = sal_True;
1368 							bNextCh = sal_False;
1369 						}
1370 						break;
1371 
1372 					case HTML_STYLE_ON:
1373 						bReadStyle = sal_True;
1374 						break;
1375 					case HTML_STYLE_OFF:
1376 						bReadStyle = sal_False;
1377 						break;
1378 					}
1379 
1380 				}
1381 			}
1382 			break;
1383 
1384 		case sal_Unicode(EOF):
1385 			if( rInput.IsEof() )
1386 			{
1387 				eState = SVPAR_ACCEPTED;
1388 				nRet = nNextCh;
1389 			}
1390 			else
1391 			{
1392 				// normalen Text lesen
1393 				goto scan_text;
1394 			}
1395 			break;
1396 
1397 		case '\f':
1398 			// Form-Feeds werden jetzt extra nach oben gereicht
1399 			nRet = HTML_LINEFEEDCHAR; // !!! eigentlich FORMFEEDCHAR
1400 			break;
1401 
1402 		case '\n':
1403 		case '\r':
1404 			if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
1405 			{
1406 				sal_Unicode c = GetNextChar();
1407 				if( ( '\n' != nNextCh || '\r' != c ) &&
1408 					( '\r' != nNextCh || '\n' != c ) )
1409 				{
1410 					bNextCh = sal_False;
1411 					nNextCh = c;
1412 				}
1413 				nRet = HTML_NEWPARA;
1414 				break;
1415 			}
1416 			// kein break !
1417 		case '\t':
1418 			if( bReadPRE )
1419 			{
1420 				nRet = HTML_TABCHAR;
1421 				break;
1422 			}
1423 			// kein break !
1424 		case ' ':
1425 			// kein break !
1426 		default:
1427 
1428 scan_text:
1429 			// es folgt "normaler" Text
1430 			nRet = ScanText();
1431 			bNextCh = 0 == aToken.Len();
1432 
1433 			// der Text sollte noch verarbeitet werden
1434 			if( !bNextCh && eState == SVPAR_PENDING )
1435 			{
1436 				eState = SVPAR_WORKING;
1437 				bReadNextChar = sal_True;
1438 			}
1439 
1440 			break;
1441 		}
1442 
1443 		if( bNextCh && SVPAR_WORKING == eState )
1444 		{
1445 			nNextCh = GetNextChar();
1446 			if( SVPAR_PENDING == eState && nRet && HTML_TEXTTOKEN != nRet )
1447 			{
1448 				bReadNextChar = sal_True;
1449 				eState = SVPAR_WORKING;
1450 			}
1451 		}
1452 
1453 	} while( !nRet && SVPAR_WORKING == eState );
1454 
1455 	if( SVPAR_PENDING == eState )
1456 		nRet = -1;		// irgendwas ungueltiges
1457 
1458 	return nRet;
1459 }
1460 
1461 void HTMLParser::UnescapeToken()
1462 {
1463 	xub_StrLen nPos=0;
1464 
1465 	sal_Bool bEscape = sal_False;
1466 	while( nPos < aToken.Len() )
1467 	{
1468 		sal_Bool bOldEscape = bEscape;
1469 		bEscape = sal_False;
1470 		if( '\\'==aToken.GetChar(nPos) && !bOldEscape )
1471 		{
1472 			aToken.Erase( nPos, 1 );
1473 			bEscape = sal_True;
1474 		}
1475 		else
1476 		{
1477 			nPos++;
1478 		}
1479 	}
1480 }
1481 
1482 // hole die Optionen
1483 const HTMLOptions *HTMLParser::GetOptions( sal_uInt16 *pNoConvertToken ) const
1484 {
1485 	// wenn die Option fuer das aktuelle Token schon einmal
1486 	// geholt wurden, geben wir sie noch einmal zurueck
1487 	if( pOptions->Count() )
1488 		return pOptions;
1489 
1490 	xub_StrLen nPos = 0;
1491 	while( nPos < aToken.Len() )
1492 	{
1493 		// ein Zeichen ? Dann faengt hier eine Option an
1494 		if( HTML_ISALPHA( aToken.GetChar(nPos) ) )
1495 		{
1496 			int nToken;
1497 			String aValue;
1498 			xub_StrLen nStt = nPos;
1499 			sal_Unicode cChar = 0;
1500 
1501 			// Eigentlich sind hier nur ganz bestimmte Zeichen erlaubt.
1502 			// Netscape achtet aber nur auf "=" und Leerzeichen (siehe
1503 			// Mozilla: PA_FetchRequestedNameValues in
1504 			// lipparse/pa_mdl.c
1505 //			while( nPos < aToken.Len() &&
1506 //					( '-'==(c=aToken[nPos]) || isalnum(c) || '.'==c || '_'==c) )
1507 			while( nPos < aToken.Len() && '=' != (cChar=aToken.GetChar(nPos)) &&
1508 				   HTML_ISPRINTABLE(cChar) && !HTML_ISSPACE(cChar) )
1509 				nPos++;
1510 
1511 			String sName( aToken.Copy( nStt, nPos-nStt ) );
1512 
1513 //JP 23.03.97: die PlugIns wollen die TokenName im "Original" haben
1514 //				also nur fuers Suchen in UpperCase wandeln
1515 			String sNameUpperCase( sName );
1516 			sNameUpperCase.ToUpperAscii();
1517 
1518 			nToken = GetHTMLOption( sNameUpperCase ); // der Name ist fertig
1519 			DBG_ASSERTWARNING( nToken!=HTML_O_UNKNOWN,
1520 						"GetOption: unbekannte HTML-Option" );
1521 			sal_Bool bStripCRLF = (nToken < HTML_OPTION_SCRIPT_START ||
1522 							   nToken >= HTML_OPTION_SCRIPT_END) &&
1523 							  (!pNoConvertToken || nToken != *pNoConvertToken);
1524 
1525 			while( nPos < aToken.Len() &&
1526 				   ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) ||
1527 					 HTML_ISSPACE(cChar) ) )
1528 				nPos++;
1529 
1530 			// hat die Option auch einen Wert?
1531 			if( nPos!=aToken.Len() && '='==cChar )
1532 			{
1533 				nPos++;
1534 
1535 				while( nPos < aToken.Len() &&
1536 						( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) ||
1537 						  ' '==cChar || '\t'==cChar || '\r'==cChar || '\n'==cChar ) )
1538 					nPos++;
1539 
1540 				if( nPos != aToken.Len() )
1541 				{
1542 					xub_StrLen nLen = 0;
1543 					nStt = nPos;
1544 					if( ('"'==cChar) || ('\'')==cChar )
1545 					{
1546 						sal_Unicode cEnd = cChar;
1547 						nPos++; nStt++;
1548 						sal_Bool bDone = sal_False;
1549 						sal_Bool bEscape = sal_False;
1550 						while( nPos < aToken.Len() && !bDone )
1551 						{
1552 							sal_Bool bOldEscape = bEscape;
1553 							bEscape = sal_False;
1554 							cChar = aToken.GetChar(nPos);
1555 							switch( cChar )
1556 							{
1557 							case '\r':
1558 							case '\n':
1559 								if( bStripCRLF )
1560 									((String &)aToken).Erase( nPos, 1 );
1561 								else
1562 									nPos++, nLen++;
1563 								break;
1564 							case '\\':
1565 								if( bOldEscape )
1566 								{
1567 									nPos++, nLen++;
1568 								}
1569 								else
1570 								{
1571 									((String &)aToken).Erase( nPos, 1 );
1572 									bEscape = sal_True;
1573 								}
1574 								break;
1575 							case '"':
1576 							case '\'':
1577 								bDone = !bOldEscape && cChar==cEnd;
1578 								if( !bDone )
1579 									nPos++, nLen++;
1580 								break;
1581 							default:
1582 								nPos++, nLen++;
1583 								break;
1584 							}
1585 						}
1586 						if( nPos!=aToken.Len() )
1587 							nPos++;
1588 					}
1589 					else
1590 					{
1591 						// hier sind wir etwas laxer als der
1592 						// Standard und erlauben alles druckbare
1593 						sal_Bool bEscape = sal_False;
1594 						sal_Bool bDone = sal_False;
1595 						while( nPos < aToken.Len() && !bDone )
1596 						{
1597 							sal_Bool bOldEscape = bEscape;
1598 							bEscape = sal_False;
1599 							sal_Unicode c = aToken.GetChar(nPos);
1600 							switch( c )
1601 							{
1602 							case ' ':
1603 								bDone = !bOldEscape;
1604 								if( !bDone )
1605 									nPos++, nLen++;
1606 								break;
1607 
1608 							case '\t':
1609 							case '\r':
1610 							case '\n':
1611 								bDone = sal_True;
1612 								break;
1613 
1614 							case '\\':
1615 								if( bOldEscape )
1616 								{
1617 									nPos++, nLen++;
1618 								}
1619 								else
1620 								{
1621 									((String &)aToken).Erase( nPos, 1 );
1622 									bEscape = sal_True;
1623 								}
1624 								break;
1625 
1626 							default:
1627 								if( HTML_ISPRINTABLE( c ) )
1628 									nPos++, nLen++;
1629 								else
1630 									bDone = sal_True;
1631 								break;
1632 							}
1633 						}
1634 					}
1635 
1636 					if( nLen )
1637 						aValue = aToken.Copy( nStt, nLen );
1638 				}
1639 			}
1640 
1641 			// Wir kennen das Token und koennen es Speichern
1642 			HTMLOption *pOption =
1643 				new HTMLOption(
1644                     sal::static_int_cast< sal_uInt16 >(nToken), sName, aValue );
1645 
1646 			pOptions->Insert( pOption, pOptions->Count() );
1647 
1648 		}
1649 		else
1650 			// white space un unerwartete Zeichen ignorieren wie
1651 			nPos++;
1652 	}
1653 
1654 	return pOptions;
1655 }
1656 
1657 int HTMLParser::FilterPRE( int nToken )
1658 {
1659 	switch( nToken )
1660 	{
1661 #ifdef HTML_BEHAVIOUR
1662 	// diese werden laut Definition zu LFs
1663 	case HTML_PARABREAK_ON:
1664 	case HTML_LINEBREAK:
1665 		nToken = HTML_NEWPARA;
1666 #else
1667 	// in Netscape zeigen sie aber nur in nicht-leeren Absaetzen Wirkung
1668 	case HTML_PARABREAK_ON:
1669 		nToken = HTML_LINEBREAK;
1670 	case HTML_LINEBREAK:
1671 #endif
1672 	case HTML_NEWPARA:
1673 		nPre_LinePos = 0;
1674 		if( bPre_IgnoreNewPara )
1675 			nToken = 0;
1676 		break;
1677 
1678 	case HTML_TABCHAR:
1679 		{
1680 			xub_StrLen nSpaces = sal::static_int_cast< xub_StrLen >(
1681                 8 - (nPre_LinePos % 8));
1682 			DBG_ASSERT( !aToken.Len(), "Wieso ist das Token nicht leer?" );
1683 			aToken.Expand( nSpaces, ' ' );
1684 			nPre_LinePos += nSpaces;
1685 			nToken = HTML_TEXTTOKEN;
1686 		}
1687 		break;
1688 	// diese bleiben erhalten
1689 	case HTML_TEXTTOKEN:
1690 		nPre_LinePos += aToken.Len();
1691 		break;
1692 
1693 	case HTML_SELECT_ON:
1694 	case HTML_SELECT_OFF:
1695 	case HTML_BODY_ON:
1696 	case HTML_FORM_ON:
1697 	case HTML_FORM_OFF:
1698 	case HTML_INPUT:
1699 	case HTML_OPTION:
1700 	case HTML_TEXTAREA_ON:
1701 	case HTML_TEXTAREA_OFF:
1702 
1703 	case HTML_IMAGE:
1704 	case HTML_APPLET_ON:
1705 	case HTML_APPLET_OFF:
1706 	case HTML_PARAM:
1707 	case HTML_EMBED:
1708 
1709 	case HTML_HEAD1_ON:
1710 	case HTML_HEAD1_OFF:
1711 	case HTML_HEAD2_ON:
1712 	case HTML_HEAD2_OFF:
1713 	case HTML_HEAD3_ON:
1714 	case HTML_HEAD3_OFF:
1715 	case HTML_HEAD4_ON:
1716 	case HTML_HEAD4_OFF:
1717 	case HTML_HEAD5_ON:
1718 	case HTML_HEAD5_OFF:
1719 	case HTML_HEAD6_ON:
1720 	case HTML_HEAD6_OFF:
1721 	case HTML_BLOCKQUOTE_ON:
1722 	case HTML_BLOCKQUOTE_OFF:
1723 	case HTML_ADDRESS_ON:
1724 	case HTML_ADDRESS_OFF:
1725 	case HTML_HORZRULE:
1726 
1727 	case HTML_CENTER_ON:
1728 	case HTML_CENTER_OFF:
1729 	case HTML_DIVISION_ON:
1730 	case HTML_DIVISION_OFF:
1731 
1732 	case HTML_SCRIPT_ON:
1733 	case HTML_SCRIPT_OFF:
1734 	case HTML_RAWDATA:
1735 
1736 	case HTML_TABLE_ON:
1737 	case HTML_TABLE_OFF:
1738 	case HTML_CAPTION_ON:
1739 	case HTML_CAPTION_OFF:
1740 	case HTML_COLGROUP_ON:
1741 	case HTML_COLGROUP_OFF:
1742 	case HTML_COL_ON:
1743 	case HTML_COL_OFF:
1744 	case HTML_THEAD_ON:
1745 	case HTML_THEAD_OFF:
1746 	case HTML_TFOOT_ON:
1747 	case HTML_TFOOT_OFF:
1748 	case HTML_TBODY_ON:
1749 	case HTML_TBODY_OFF:
1750 	case HTML_TABLEROW_ON:
1751 	case HTML_TABLEROW_OFF:
1752 	case HTML_TABLEDATA_ON:
1753 	case HTML_TABLEDATA_OFF:
1754 	case HTML_TABLEHEADER_ON:
1755 	case HTML_TABLEHEADER_OFF:
1756 
1757 	case HTML_ANCHOR_ON:
1758 	case HTML_ANCHOR_OFF:
1759 	case HTML_BOLD_ON:
1760 	case HTML_BOLD_OFF:
1761 	case HTML_ITALIC_ON:
1762 	case HTML_ITALIC_OFF:
1763 	case HTML_STRIKE_ON:
1764 	case HTML_STRIKE_OFF:
1765 	case HTML_STRIKETHROUGH_ON:
1766 	case HTML_STRIKETHROUGH_OFF:
1767 	case HTML_UNDERLINE_ON:
1768 	case HTML_UNDERLINE_OFF:
1769 	case HTML_BASEFONT_ON:
1770 	case HTML_BASEFONT_OFF:
1771 	case HTML_FONT_ON:
1772 	case HTML_FONT_OFF:
1773 	case HTML_BLINK_ON:
1774 	case HTML_BLINK_OFF:
1775 	case HTML_SPAN_ON:
1776 	case HTML_SPAN_OFF:
1777 	case HTML_SUBSCRIPT_ON:
1778 	case HTML_SUBSCRIPT_OFF:
1779 	case HTML_SUPERSCRIPT_ON:
1780 	case HTML_SUPERSCRIPT_OFF:
1781 	case HTML_BIGPRINT_ON:
1782 	case HTML_BIGPRINT_OFF:
1783 	case HTML_SMALLPRINT_OFF:
1784 	case HTML_SMALLPRINT_ON:
1785 
1786 	case HTML_EMPHASIS_ON:
1787 	case HTML_EMPHASIS_OFF:
1788 	case HTML_CITIATION_ON:
1789 	case HTML_CITIATION_OFF:
1790 	case HTML_STRONG_ON:
1791 	case HTML_STRONG_OFF:
1792 	case HTML_CODE_ON:
1793 	case HTML_CODE_OFF:
1794 	case HTML_SAMPLE_ON:
1795 	case HTML_SAMPLE_OFF:
1796 	case HTML_KEYBOARD_ON:
1797 	case HTML_KEYBOARD_OFF:
1798 	case HTML_VARIABLE_ON:
1799 	case HTML_VARIABLE_OFF:
1800 	case HTML_DEFINSTANCE_ON:
1801 	case HTML_DEFINSTANCE_OFF:
1802 	case HTML_SHORTQUOTE_ON:
1803 	case HTML_SHORTQUOTE_OFF:
1804 	case HTML_LANGUAGE_ON:
1805 	case HTML_LANGUAGE_OFF:
1806 	case HTML_AUTHOR_ON:
1807 	case HTML_AUTHOR_OFF:
1808 	case HTML_PERSON_ON:
1809 	case HTML_PERSON_OFF:
1810 	case HTML_ACRONYM_ON:
1811 	case HTML_ACRONYM_OFF:
1812 	case HTML_ABBREVIATION_ON:
1813 	case HTML_ABBREVIATION_OFF:
1814 	case HTML_INSERTEDTEXT_ON:
1815 	case HTML_INSERTEDTEXT_OFF:
1816 	case HTML_DELETEDTEXT_ON:
1817 	case HTML_DELETEDTEXT_OFF:
1818 	case HTML_TELETYPE_ON:
1819 	case HTML_TELETYPE_OFF:
1820 
1821 		break;
1822 
1823 	// der Rest wird als unbekanntes Token behandelt
1824 	default:
1825 		if( nToken )
1826 		{
1827 			nToken =
1828 				( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
1829 					? HTML_UNKNOWNCONTROL_OFF
1830 					: HTML_UNKNOWNCONTROL_ON );
1831 		}
1832 		break;
1833 	}
1834 
1835 	bPre_IgnoreNewPara = sal_False;
1836 
1837 	return nToken;
1838 }
1839 
1840 int HTMLParser::FilterXMP( int nToken )
1841 {
1842 	switch( nToken )
1843 	{
1844 	case HTML_NEWPARA:
1845 		if( bPre_IgnoreNewPara )
1846 			nToken = 0;
1847 	case HTML_TEXTTOKEN:
1848 	case HTML_NONBREAKSPACE:
1849 	case HTML_SOFTHYPH:
1850 		break;				// bleiben erhalten
1851 
1852 	default:
1853 		if( nToken )
1854 		{
1855 			if( (HTML_TOKEN_ONOFF & nToken) && (1 & nToken) )
1856 			{
1857 				sSaveToken.Insert( '<', 0 );
1858 				sSaveToken.Insert( '/', 1 );
1859 			}
1860 			else
1861 				sSaveToken.Insert( '<', 0 );
1862 			if( aToken.Len() )
1863 			{
1864 				UnescapeToken();
1865 				sSaveToken += (sal_Unicode)' ';
1866 				aToken.Insert( sSaveToken, 0 );
1867 			}
1868 			else
1869 				aToken = sSaveToken;
1870 			aToken += (sal_Unicode)'>';
1871 			nToken = HTML_TEXTTOKEN;
1872 		}
1873 		break;
1874 	}
1875 
1876 	bPre_IgnoreNewPara = sal_False;
1877 
1878 	return nToken;
1879 }
1880 
1881 int HTMLParser::FilterListing( int nToken )
1882 {
1883 	switch( nToken )
1884 	{
1885 	case HTML_NEWPARA:
1886 		if( bPre_IgnoreNewPara )
1887 			nToken = 0;
1888 	case HTML_TEXTTOKEN:
1889 	case HTML_NONBREAKSPACE:
1890 	case HTML_SOFTHYPH:
1891 		break;		// bleiben erhalten
1892 
1893 	default:
1894 		if( nToken )
1895 		{
1896 			nToken =
1897 				( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
1898 					? HTML_UNKNOWNCONTROL_OFF
1899 					: HTML_UNKNOWNCONTROL_ON );
1900 		}
1901 		break;
1902 	}
1903 
1904 	bPre_IgnoreNewPara = sal_False;
1905 
1906 	return nToken;
1907 }
1908 
1909 FASTBOOL HTMLParser::IsHTMLFormat( const sal_Char* pHeader,
1910 								   sal_Bool bSwitchToUCS2,
1911 								   rtl_TextEncoding eEnc )
1912 {
1913 	// Einer der folgenden regulaeren Ausdrucke muss sich auf den String
1914 	// anwenden lassen, damit das Dok ein HTML-Dokument ist.
1915 	//
1916 	// ^[^<]*<[^ \t]*[> \t]
1917 	//        -------
1918 	// ^<!
1919 	//
1920 	// wobei der unterstrichene Teilausdruck einem HTML-Token
1921 	// ensprechen muss
1922 
1923 	ByteString sCmp;
1924 	sal_Bool bUCS2B = sal_False;
1925 	if( bSwitchToUCS2 )
1926 	{
1927 		if( 0xfeU == (sal_uChar)pHeader[0] &&
1928 			0xffU == (sal_uChar)pHeader[1] )
1929 		{
1930 			eEnc = RTL_TEXTENCODING_UCS2;
1931 			bUCS2B = sal_True;
1932 		}
1933 		else if( 0xffU == (sal_uChar)pHeader[0] &&
1934 				 0xfeU == (sal_uChar)pHeader[1] )
1935 		{
1936 			eEnc = RTL_TEXTENCODING_UCS2;
1937 		}
1938 	}
1939 	if
1940        (
1941         RTL_TEXTENCODING_UCS2 == eEnc &&
1942         (
1943          (0xfe == (sal_uChar)pHeader[0] && 0xff == (sal_uChar)pHeader[1]) ||
1944          (0xff == (sal_uChar)pHeader[0] && 0xfe == (sal_uChar)pHeader[1])
1945         )
1946        )
1947 	{
1948 		if( 0xfe == (sal_uChar)pHeader[0] )
1949 			bUCS2B = sal_True;
1950 
1951 		xub_StrLen nLen;
1952 		for( nLen = 2;
1953 			 pHeader[nLen] != 0 || pHeader[nLen+1] != 0;
1954 			 nLen+=2 )
1955 			;
1956 
1957 		::rtl::OStringBuffer sTmp( (nLen - 2)/2 );
1958 		for( xub_StrLen nPos = 2; nPos < nLen; nPos += 2 )
1959 		{
1960 			sal_Unicode cUC;
1961 			if( bUCS2B )
1962 				cUC = (sal_Unicode(pHeader[nPos]) << 8) | pHeader[nPos+1];
1963 			else
1964 				cUC = (sal_Unicode(pHeader[nPos+1]) << 8) | pHeader[nPos];
1965 			if( 0U == cUC )
1966 				break;
1967 
1968 			sTmp.append( cUC < 256U ? (sal_Char)cUC : '.' );
1969 		}
1970 		sCmp = ByteString( sTmp.makeStringAndClear() );
1971 	}
1972 	else
1973 	{
1974 		sCmp = (sal_Char *)pHeader;
1975 	}
1976 
1977 	sCmp.ToUpperAscii();
1978 
1979 	// Ein HTML-Dokument muss in der ersten Zeile ein '<' besitzen
1980 	xub_StrLen nStart = sCmp.Search( '<' );
1981 	if( STRING_NOTFOUND  == nStart )
1982 		return sal_False;
1983 	nStart++;
1984 
1985 	// danach duerfen beliebige andere Zeichen bis zu einem blank oder
1986 	// '>' kommen
1987 	sal_Char c;
1988 	xub_StrLen nPos;
1989 	for( nPos = nStart; nPos<sCmp.Len(); nPos++ )
1990 	{
1991 		if( '>'==(c=sCmp.GetChar(nPos)) || HTML_ISSPACE(c) )
1992 			break;
1993 	}
1994 
1995 	// wenn das Dokeument hinter dem < aufhoert ist es wohl kein HTML
1996 	if( nPos==nStart )
1997 		return sal_False;
1998 
1999 	// die Zeichenkette nach dem '<' muss ausserdem ein bekanntes
2000 	// HTML Token sein. Damit die Ausgabe eines DOS-dir-Befehls nicht
2001 	// als HTML interpretiert wird, wird ein <DIR> jedoch nicht als HTML
2002 	// interpretiert.
2003 	String sTest( sCmp.Copy( nStart, nPos-nStart ), RTL_TEXTENCODING_ASCII_US );
2004 	int nTok = GetHTMLToken( sTest );
2005 	if( 0 != nTok && HTML_DIRLIST_ON != nTok )
2006 		return sal_True;
2007 
2008 	// oder es handelt sich um ein "<!" ganz am Anfang der Datei (fix #27092#)
2009 	if( nStart == 1 && '!' == sCmp.GetChar( 1 ) )
2010 		return sal_True;
2011 
2012 	// oder wir finden irgendwo ein <HTML> in den ersten 80 Zeichen
2013 	nStart = sCmp.Search( OOO_STRING_SVTOOLS_HTML_html );
2014 	if( nStart!=STRING_NOTFOUND &&
2015 		nStart>0 && '<'==sCmp.GetChar(nStart-1) &&
2016 		nStart+4 < sCmp.Len() && '>'==sCmp.GetChar(nStart+4) )
2017 		return sal_True;
2018 
2019 	// sonst ist es wohl doch eher kein HTML-Dokument
2020 	return sal_False;
2021 }
2022 
2023 sal_Bool HTMLParser::InternalImgToPrivateURL( String& rURL )
2024 {
2025 	if( rURL.Len() < 19 || 'i' != rURL.GetChar(0) ||
2026 		rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher, 9 ) != COMPARE_EQUAL )
2027 		return sal_False;
2028 
2029 	sal_Bool bFound = sal_False;
2030 
2031 	if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher,16) == COMPARE_EQUAL )
2032 	{
2033 		String aName( rURL.Copy(16) );
2034 		switch( aName.GetChar(0) )
2035 		{
2036 		case 'b':
2037 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_binary );
2038 			break;
2039 		case 'i':
2040 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_image ) ||
2041 					 aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_index );
2042 			break;
2043 		case 'm':
2044 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_menu ) ||
2045 					 aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_movie );
2046 			break;
2047 		case 's':
2048 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_sound );
2049 			break;
2050 		case 't':
2051 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_telnet ) ||
2052 					 aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_text );
2053 			break;
2054 		case 'u':
2055 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_unknown );
2056 			break;
2057 		}
2058 	}
2059 	else if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_icon,14) == COMPARE_EQUAL )
2060 	{
2061 		String aName( rURL.Copy(14) );
2062 		switch( aName.GetChar(0) )
2063 		{
2064 		case 'b':
2065 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_baddata );
2066 			break;
2067 		case 'd':
2068 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_delayed );
2069 			break;
2070 		case 'e':
2071 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_embed );
2072 			break;
2073 		case 'i':
2074 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_insecure );
2075 			break;
2076 		case 'n':
2077 			bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_notfound );
2078 			break;
2079 		}
2080 	}
2081 	if( bFound )
2082 	{
2083 		String sTmp ( rURL );
2084 		rURL.AssignAscii( OOO_STRING_SVTOOLS_HTML_private_image );
2085 		rURL.Append( sTmp );
2086 	}
2087 
2088 	return bFound;
2089 }
2090 
2091 #ifdef USED
2092 void HTMLParser::SaveState( int nToken )
2093 {
2094 	SvParser::SaveState( nToken );
2095 }
2096 
2097 void HTMLParser::RestoreState()
2098 {
2099 	SvParser::RestoreState();
2100 }
2101 #endif
2102 
2103 
2104 enum eHtmlMetas {
2105     HTML_META_NONE = 0,
2106     HTML_META_AUTHOR,
2107     HTML_META_DESCRIPTION,
2108     HTML_META_KEYWORDS,
2109     HTML_META_REFRESH,
2110     HTML_META_CLASSIFICATION,
2111     HTML_META_CREATED,
2112     HTML_META_CHANGEDBY,
2113     HTML_META_CHANGED,
2114     HTML_META_GENERATOR,
2115     HTML_META_SDFOOTNOTE,
2116     HTML_META_SDENDNOTE,
2117     HTML_META_CONTENT_TYPE
2118 };
2119 
2120 // <META NAME=xxx>
2121 static HTMLOptionEnum __READONLY_DATA aHTMLMetaNameTable[] =
2122 {
2123     { OOO_STRING_SVTOOLS_HTML_META_author,        HTML_META_AUTHOR        },
2124     { OOO_STRING_SVTOOLS_HTML_META_changed,       HTML_META_CHANGED       },
2125     { OOO_STRING_SVTOOLS_HTML_META_changedby,     HTML_META_CHANGEDBY     },
2126     { OOO_STRING_SVTOOLS_HTML_META_classification,HTML_META_CLASSIFICATION},
2127     { OOO_STRING_SVTOOLS_HTML_META_content_type,  HTML_META_CONTENT_TYPE  },
2128     { OOO_STRING_SVTOOLS_HTML_META_created,       HTML_META_CREATED       },
2129     { OOO_STRING_SVTOOLS_HTML_META_description,   HTML_META_DESCRIPTION   },
2130     { OOO_STRING_SVTOOLS_HTML_META_keywords,      HTML_META_KEYWORDS      },
2131     { OOO_STRING_SVTOOLS_HTML_META_generator,     HTML_META_GENERATOR     },
2132     { OOO_STRING_SVTOOLS_HTML_META_refresh,       HTML_META_REFRESH       },
2133     { OOO_STRING_SVTOOLS_HTML_META_sdendnote,     HTML_META_SDENDNOTE     },
2134     { OOO_STRING_SVTOOLS_HTML_META_sdfootnote,    HTML_META_SDFOOTNOTE    },
2135     { 0,                                          0                       }
2136 };
2137 
2138 
2139 void HTMLParser::AddMetaUserDefined( ::rtl::OUString const & )
2140 {
2141 }
2142 
2143 bool HTMLParser::ParseMetaOptionsImpl(
2144         const uno::Reference<document::XDocumentProperties> & i_xDocProps,
2145         SvKeyValueIterator *i_pHTTPHeader,
2146         const HTMLOptions *i_pOptions,
2147         rtl_TextEncoding& o_rEnc )
2148 {
2149     String aName, aContent;
2150     sal_uInt16 nAction = HTML_META_NONE;
2151     bool bHTTPEquiv = false, bChanged = false;
2152 
2153     for ( sal_uInt16 i = i_pOptions->Count(); i; )
2154     {
2155         const HTMLOption *pOption = (*i_pOptions)[ --i ];
2156         switch ( pOption->GetToken() )
2157         {
2158             case HTML_O_NAME:
2159                 aName = pOption->GetString();
2160                 if ( HTML_META_NONE==nAction )
2161                 {
2162                     pOption->GetEnum( nAction, aHTMLMetaNameTable );
2163                 }
2164                 break;
2165             case HTML_O_HTTPEQUIV:
2166                 aName = pOption->GetString();
2167                 pOption->GetEnum( nAction, aHTMLMetaNameTable );
2168                 bHTTPEquiv = true;
2169                 break;
2170             case HTML_O_CONTENT:
2171                 aContent = pOption->GetString();
2172                 break;
2173         }
2174     }
2175 
2176     if ( bHTTPEquiv || HTML_META_DESCRIPTION != nAction )
2177     {
2178         // if it is not a Description, remove CRs and LFs from CONTENT
2179         aContent.EraseAllChars( _CR );
2180         aContent.EraseAllChars( _LF );
2181     }
2182     else
2183     {
2184         // convert line endings for Description
2185         aContent.ConvertLineEnd();
2186     }
2187 
2188 
2189     if ( bHTTPEquiv && i_pHTTPHeader )
2190     {
2191         // #57232#: Netscape seems to just ignore a closing ", so we do too
2192         if ( aContent.Len() && '"' == aContent.GetChar( aContent.Len()-1 ) )
2193         {
2194             aContent.Erase( aContent.Len() - 1 );
2195         }
2196         SvKeyValue aKeyValue( aName, aContent );
2197         i_pHTTPHeader->Append( aKeyValue );
2198     }
2199 
2200     switch ( nAction )
2201     {
2202         case HTML_META_AUTHOR:
2203             if (i_xDocProps.is()) {
2204                 i_xDocProps->setAuthor( aContent );
2205                 bChanged = true;
2206             }
2207             break;
2208         case HTML_META_DESCRIPTION:
2209             if (i_xDocProps.is()) {
2210                 i_xDocProps->setDescription( aContent );
2211                 bChanged = true;
2212             }
2213             break;
2214         case HTML_META_KEYWORDS:
2215             if (i_xDocProps.is()) {
2216                 i_xDocProps->setKeywords(
2217                     ::comphelper::string::convertCommaSeparated(aContent));
2218                 bChanged = true;
2219             }
2220             break;
2221         case HTML_META_CLASSIFICATION:
2222             if (i_xDocProps.is()) {
2223                 i_xDocProps->setSubject( aContent );
2224                 bChanged = true;
2225             }
2226             break;
2227 
2228         case HTML_META_CHANGEDBY:
2229             if (i_xDocProps.is()) {
2230                 i_xDocProps->setModifiedBy( aContent );
2231             }
2232             break;
2233 
2234         case HTML_META_CREATED:
2235         case HTML_META_CHANGED:
2236             if ( i_xDocProps.is() && aContent.Len() &&
2237                  aContent.GetTokenCount() == 2 )
2238             {
2239                 Date aDate( (sal_uLong)aContent.GetToken(0).ToInt32() );
2240                 Time aTime( (sal_uLong)aContent.GetToken(1).ToInt32() );
2241                 DateTime aDateTime( aDate, aTime );
2242                 ::util::DateTime uDT(aDateTime.Get100Sec(),
2243                     aDateTime.GetSec(), aDateTime.GetMin(),
2244                     aDateTime.GetHour(), aDateTime.GetDay(),
2245                     aDateTime.GetMonth(), aDateTime.GetYear());
2246                 if ( HTML_META_CREATED==nAction )
2247                     i_xDocProps->setCreationDate( uDT );
2248                 else
2249                     i_xDocProps->setModificationDate( uDT );
2250                 bChanged = true;
2251             }
2252             break;
2253 
2254         case HTML_META_REFRESH:
2255             DBG_ASSERT( !bHTTPEquiv || i_pHTTPHeader,
2256         "Reload-URL aufgrund unterlassener MUSS-Aenderung verlorengegangen" );
2257             break;
2258 
2259         case HTML_META_CONTENT_TYPE:
2260             if ( aContent.Len() )
2261             {
2262                 o_rEnc = GetEncodingByMIME( aContent );
2263             }
2264             break;
2265 
2266         case HTML_META_NONE:
2267             if ( !bHTTPEquiv )
2268             {
2269                 if (i_xDocProps.is())
2270                 {
2271                     uno::Reference<beans::XPropertyContainer> xUDProps
2272                         = i_xDocProps->getUserDefinedProperties();
2273                     try {
2274                         xUDProps->addProperty(aName,
2275                             beans::PropertyAttribute::REMOVEABLE,
2276                             uno::makeAny(::rtl::OUString(aContent)));
2277                         AddMetaUserDefined(aName);
2278                         bChanged = true;
2279                     } catch (uno::Exception &) {
2280                         // ignore
2281                     }
2282                 }
2283             }
2284             break;
2285         default:
2286             break;
2287     }
2288 
2289     return bChanged;
2290 }
2291 
2292 bool HTMLParser::ParseMetaOptions(
2293         const uno::Reference<document::XDocumentProperties> & i_xDocProps,
2294         SvKeyValueIterator *i_pHeader )
2295 {
2296     sal_uInt16 nContentOption = HTML_O_CONTENT;
2297     rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
2298 
2299     bool bRet = ParseMetaOptionsImpl( i_xDocProps, i_pHeader,
2300 				      GetOptions(&nContentOption),
2301 				      eEnc );
2302 
2303     // If the encoding is set by a META tag, it may only overwrite the
2304     // current encoding if both, the current and the new encoding, are 1-sal_uInt8
2305     // encodings. Everything else cannot lead to reasonable results.
2306     if (RTL_TEXTENCODING_DONTKNOW != eEnc &&
2307         rtl_isOctetTextEncoding( eEnc ) &&
2308         rtl_isOctetTextEncoding( GetSrcEncoding() ) )
2309     {
2310         eEnc = GetExtendedCompatibilityTextEncoding( eEnc ); // #89973#
2311         SetSrcEncoding( eEnc );
2312     }
2313 
2314     return bRet;
2315 }
2316 
2317 rtl_TextEncoding HTMLParser::GetEncodingByMIME( const String& rMime )
2318 {
2319     ByteString sType;
2320     ByteString sSubType;
2321     INetContentTypeParameterList aParameters;
2322     ByteString sMime( rMime, RTL_TEXTENCODING_ASCII_US );
2323     if (INetContentTypes::parse(sMime, sType, sSubType, &aParameters))
2324     {
2325         const INetContentTypeParameter * pCharset
2326             = aParameters.find("charset");
2327         if (pCharset != 0)
2328         {
2329             ByteString sValue( pCharset->m_sValue, RTL_TEXTENCODING_ASCII_US );
2330             return GetExtendedCompatibilityTextEncoding(
2331                     rtl_getTextEncodingFromMimeCharset( sValue.GetBuffer() ) );
2332         }
2333     }
2334     return RTL_TEXTENCODING_DONTKNOW;
2335 }
2336 
2337 rtl_TextEncoding HTMLParser::GetEncodingByHttpHeader( SvKeyValueIterator *pHTTPHeader )
2338 {
2339     rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW;
2340     if( pHTTPHeader )
2341 	{
2342         SvKeyValue aKV;
2343 		for( sal_Bool bCont = pHTTPHeader->GetFirst( aKV ); bCont;
2344 			 bCont = pHTTPHeader->GetNext( aKV ) )
2345 		{
2346 			if( aKV.GetKey().EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_META_content_type ) )
2347 			{
2348 				if( aKV.GetValue().Len() )
2349 				{
2350                     eRet = HTMLParser::GetEncodingByMIME( aKV.GetValue() );
2351                 }
2352 			}
2353 		}
2354 	}
2355     return eRet;
2356 }
2357 
2358 sal_Bool HTMLParser::SetEncodingByHTTPHeader(
2359 								SvKeyValueIterator *pHTTPHeader )
2360 {
2361 	sal_Bool bRet = sal_False;
2362     rtl_TextEncoding eEnc = HTMLParser::GetEncodingByHttpHeader( pHTTPHeader );
2363     if(RTL_TEXTENCODING_DONTKNOW != eEnc)
2364 	{
2365         SetSrcEncoding( eEnc );
2366         bRet = sal_True;
2367     }
2368 	return bRet;
2369 }
2370 
2371 
2372