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