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 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 129 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 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 156 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 170 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 180 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 250 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 292 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 298 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 304 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 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 327 HTMLParser::~HTMLParser() 328 { 329 if( pOptions && pOptions->Count() ) 330 pOptions->DeleteAndDestroy( 0, pOptions->Count() ); 331 delete pOptions; 332 } 333 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 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 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 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 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, 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 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 1482 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 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 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 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 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 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 2091 void HTMLParser::SaveState( int nToken ) 2092 { 2093 SvParser::SaveState( nToken ); 2094 } 2095 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 2138 void HTMLParser::AddMetaUserDefined( ::rtl::OUString const & ) 2139 { 2140 } 2141 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 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 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 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 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