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