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