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