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