/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; const sal_Int32 MAX_LEN( 1024L ); const sal_Int32 MAX_MACRO_LEN( 1024 ); const sal_Int32 MAX_ENTITY_LEN( 8L ); // Tables to convert option values into strings // static HTMLOptionEnum const aInputTypeOptEnums[] = { { OOO_STRING_SVTOOLS_HTML_IT_text, HTML_IT_TEXT }, { OOO_STRING_SVTOOLS_HTML_IT_password, HTML_IT_PASSWORD }, { OOO_STRING_SVTOOLS_HTML_IT_checkbox, HTML_IT_CHECKBOX }, { OOO_STRING_SVTOOLS_HTML_IT_radio, HTML_IT_RADIO }, { OOO_STRING_SVTOOLS_HTML_IT_range, HTML_IT_RANGE }, { OOO_STRING_SVTOOLS_HTML_IT_scribble, HTML_IT_SCRIBBLE }, { OOO_STRING_SVTOOLS_HTML_IT_file, HTML_IT_FILE }, { OOO_STRING_SVTOOLS_HTML_IT_hidden, HTML_IT_HIDDEN }, { OOO_STRING_SVTOOLS_HTML_IT_submit, HTML_IT_SUBMIT }, { OOO_STRING_SVTOOLS_HTML_IT_image, HTML_IT_IMAGE }, { OOO_STRING_SVTOOLS_HTML_IT_reset, HTML_IT_RESET }, { OOO_STRING_SVTOOLS_HTML_IT_button, HTML_IT_BUTTON }, { 0, 0 } }; // static HTMLOptionEnum const aTableFrameOptEnums[] = { { OOO_STRING_SVTOOLS_HTML_TF_void, HTML_TF_VOID }, { OOO_STRING_SVTOOLS_HTML_TF_above, HTML_TF_ABOVE }, { OOO_STRING_SVTOOLS_HTML_TF_below, HTML_TF_BELOW }, { OOO_STRING_SVTOOLS_HTML_TF_hsides, HTML_TF_HSIDES }, { OOO_STRING_SVTOOLS_HTML_TF_lhs, HTML_TF_LHS }, { OOO_STRING_SVTOOLS_HTML_TF_rhs, HTML_TF_RHS }, { OOO_STRING_SVTOOLS_HTML_TF_vsides, HTML_TF_VSIDES }, { OOO_STRING_SVTOOLS_HTML_TF_box, HTML_TF_BOX }, { OOO_STRING_SVTOOLS_HTML_TF_border, HTML_TF_BOX }, { 0, 0 } }; //
static HTMLOptionEnum const aTableRulesOptEnums[] = { { OOO_STRING_SVTOOLS_HTML_TR_none, HTML_TR_NONE }, { OOO_STRING_SVTOOLS_HTML_TR_groups, HTML_TR_GROUPS }, { OOO_STRING_SVTOOLS_HTML_TR_rows, HTML_TR_ROWS }, { OOO_STRING_SVTOOLS_HTML_TR_cols, HTML_TR_COLS }, { OOO_STRING_SVTOOLS_HTML_TR_all, HTML_TR_ALL }, { 0, 0 } }; sal_uInt16 HTMLOption::GetEnum( const HTMLOptionEnum *pOptEnums, sal_uInt16 nDflt ) const { sal_uInt16 nValue = nDflt; while( pOptEnums->pName ) if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) ) break; else pOptEnums++; if( pOptEnums->pName ) nValue = pOptEnums->nValue; return nValue; } bool HTMLOption::GetEnum( sal_uInt16 &rEnum, const HTMLOptionEnum *pOptEnums ) const { while( pOptEnums->pName ) { if( aValue.EqualsIgnoreCaseAscii( pOptEnums->pName ) ) break; else pOptEnums++; } const sal_Char *pName = pOptEnums->pName; if( pName ) rEnum = pOptEnums->nValue; return (pName != 0); } HTMLOption::HTMLOption( sal_uInt16 nTok, const String& rToken, const String& rValue ) : aValue(rValue) , aToken(rToken) , nToken( nTok ) { DBG_ASSERT( nToken>=HTML_OPTION_START && nToken=HTML_OPTION_NUMBER_START && nToken=HTML_OPTION_CONTEXT_START && nToken= 0 ? (sal_uInt32)nTmp : 0; } sal_Int32 HTMLOption::GetSNumber() const { DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START && nToken=HTML_OPTION_CONTEXT_START && nToken &rNumbers, bool bSpaceDelim ) const { rNumbers.clear(); if( bSpaceDelim ) { // This is a very simplified scanner: it only searches all // numerals in the string. bool bInNum = false; sal_uLong nNum = 0; for( xub_StrLen i=0; i='0' && c<='9' ) { nNum *= 10; nNum += (c - '0'); bInNum = true; } else if( bInNum ) { rNumbers.push_back( nNum ); bInNum = false; nNum = 0; } } if( bInNum ) { rNumbers.push_back( nNum ); } } else { // Check whether numbers are separated by ',' and // insert 0 if necessary xub_StrLen nPos = 0; while( nPos < aValue.Len() ) { register sal_Unicode c; while( nPos < aValue.Len() && ((c=aValue.GetChar(nPos)) == ' ' || c == '\t' || c == '\n' || c== '\r' ) ) nPos++; if( nPos==aValue.Len() ) rNumbers.push_back(0); else { xub_StrLen nEnd = aValue.Search( (sal_Unicode)',', nPos ); if( STRING_NOTFOUND==nEnd ) { sal_Int32 nTmp = aValue.Copy(nPos).ToInt32(); rNumbers.push_back( nTmp >= 0 ? (sal_uInt32)nTmp : 0 ); nPos = aValue.Len(); } else { sal_Int32 nTmp = aValue.Copy(nPos,nEnd-nPos).ToInt32(); rNumbers.push_back( nTmp >= 0 ? (sal_uInt32)nTmp : 0 ); nPos = nEnd+1; } } } } } void HTMLOption::GetColor( Color& rColor ) const { DBG_ASSERT( (nToken>=HTML_OPTION_COLOR_START && nToken= '0' && c <= '9' ) nColor += (c - 48); else if( c >= 'A' && c <= 'F' ) nColor += (c - 55); } } rColor.SetRed( (sal_uInt8)((nColor & 0x00ff0000) >> 16) ); rColor.SetGreen( (sal_uInt8)((nColor & 0x0000ff00) >> 8)); rColor.SetBlue( (sal_uInt8)(nColor & 0x000000ff) ); } HTMLInputType HTMLOption::GetInputType() const { DBG_ASSERT( nToken==HTML_O_TYPE, "GetInputType: Option not TYPE" ); return (HTMLInputType)GetEnum( aInputTypeOptEnums, HTML_IT_TEXT ); } HTMLTableFrame HTMLOption::GetTableFrame() const { DBG_ASSERT( nToken==HTML_O_FRAME, "GetTableFrame: Option not FRAME" ); return (HTMLTableFrame)GetEnum( aTableFrameOptEnums, HTML_TF_VOID ); } HTMLTableRules HTMLOption::GetTableRules() const { DBG_ASSERT( nToken==HTML_O_RULES, "GetTableRules: Option not RULES" ); return (HTMLTableRules)GetEnum( aTableRulesOptEnums, HTML_TR_NONE ); } HTMLParser::HTMLParser( SvStream& rIn, bool bReadNewDoc ) : SvParser( rIn ), bNewDoc(bReadNewDoc), bIsInHeader(true), bIsInBody(false), bReadListing(false), bReadXMP(false), bReadPRE(false), bReadTextArea(false), bReadScript(false), bReadStyle(false), bEndTokenFound(false), bPre_IgnoreNewPara(false), bReadNextChar(false), bReadComment(false) { //#i76649, default to UTF-8 for HTML unless we know differently SetSrcEncoding(RTL_TEXTENCODING_UTF8); } HTMLParser::~HTMLParser() { } SvParserState HTMLParser::CallParser() { eState = SVPAR_WORKING; nNextCh = GetNextChar(); SaveState( 0 ); nPre_LinePos = 0; bPre_IgnoreNewPara = false; AddRef(); Continue( 0 ); if( SVPAR_PENDING != eState ) ReleaseRef(); // Parser not needed anymore return eState; } void HTMLParser::Continue( int nToken ) { if( !nToken ) nToken = GetNextToken(); while( IsParserWorking() ) { SaveState( nToken ); nToken = FilterToken( nToken ); if( nToken ) NextToken( nToken ); if( IsParserWorking() ) SaveState( 0 ); // continue with new token nToken = GetNextToken(); } } int HTMLParser::FilterToken( int nToken ) { switch( nToken ) { case sal_Unicode(EOF): nToken = 0; break; // don't pass case HTML_HEAD_OFF: bIsInBody = true; case HTML_HEAD_ON: bIsInHeader = HTML_HEAD_ON == nToken; break; case HTML_BODY_ON: case HTML_FRAMESET_ON: bIsInHeader = false; bIsInBody = HTML_BODY_ON == nToken; break; case HTML_BODY_OFF: bIsInBody = bReadPRE = bReadListing = bReadXMP = false; break; case HTML_HTML_OFF: nToken = 0; bReadPRE = bReadListing = bReadXMP = false; break; // HTML_ON hasn't been passed either ! case HTML_PREFORMTXT_ON: StartPRE(); break; case HTML_PREFORMTXT_OFF: FinishPRE(); break; case HTML_LISTING_ON: StartListing(); break; case HTML_LISTING_OFF: FinishListing(); break; case HTML_XMP_ON: StartXMP(); break; case HTML_XMP_OFF: FinishXMP(); break; default: if( bReadPRE ) nToken = FilterPRE( nToken ); else if( bReadListing ) nToken = FilterListing( nToken ); else if( bReadXMP ) nToken = FilterXMP( nToken ); break; } return nToken; } #define HTML_ISDIGIT( c ) comphelper::string::isdigitAscii(c) #define HTML_ISALPHA( c ) comphelper::string::isalphaAscii(c) #define HTML_ISALNUM( c ) comphelper::string::isalnumAscii(c) #define HTML_ISSPACE( c ) ( ' ' == c || (c >= 0x09 && c <= 0x0d) ) #define HTML_ISPRINTABLE( c ) ( c >= 32 && c != 127) #define HTML_ISHEXDIGIT( c ) comphelper::string::isxdigitAscii(c) int HTMLParser::ScanText( const sal_Unicode cBreak ) { ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN ); int bContinue = true; int bEqSignFound = false; sal_Unicode cQuote = 0U; while( bContinue && IsParserWorking() ) { int bNextCh = true; switch( nNextCh ) { case '&': bEqSignFound = false; if( bReadXMP ) sTmpBuffer.append( (sal_Unicode)'&' ); else { sal_uLong nStreamPos = rInput.Tell(); sal_uLong nLinePos = GetLinePos(); sal_Unicode cChar = 0U; if( '#' == (nNextCh = GetNextChar()) ) { nNextCh = GetNextChar(); const bool bIsHex( 'x' == nNextCh ); const bool bIsDecOrHex( bIsHex || HTML_ISDIGIT(nNextCh) ); if ( bIsDecOrHex ) { if ( bIsHex ) { nNextCh = GetNextChar(); while ( HTML_ISHEXDIGIT(nNextCh) ) { cChar = cChar * 16U + ( nNextCh <= '9' ? sal_Unicode( nNextCh - '0' ) : ( nNextCh <= 'F' ? sal_Unicode( nNextCh - 'A' + 10 ) : sal_Unicode( nNextCh - 'a' + 10 ) ) ); nNextCh = GetNextChar(); } } else { do { cChar = cChar * 10U + sal_Unicode( nNextCh - '0'); nNextCh = GetNextChar(); } while( HTML_ISDIGIT(nNextCh) ); } if( RTL_TEXTENCODING_DONTKNOW != eSrcEnc && RTL_TEXTENCODING_UCS2 != eSrcEnc && RTL_TEXTENCODING_UTF8 != eSrcEnc && cChar < 256 ) { const sal_uInt32 convertFlags = RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT; sal_Char cEncodedChar = static_cast(cChar); cChar = rtl::OUString(&cEncodedChar, 1, eSrcEnc, convertFlags).toChar(); if( 0U == cChar ) { // If the character could not be // converted, because a conversion is not // available, do no conversion at all. cChar = cEncodedChar; } } } else nNextCh = 0U; } else if( HTML_ISALPHA( nNextCh ) ) { ::rtl::OUStringBuffer sEntityBuffer( MAX_ENTITY_LEN ); xub_StrLen nPos = 0L; do { sEntityBuffer.append( nNextCh ); nPos++; nNextCh = GetNextChar(); } while( nPos < MAX_ENTITY_LEN && HTML_ISALNUM( nNextCh ) && !rInput.IsEof() ); if( IsParserWorking() && !rInput.IsEof() ) { String sEntity( sEntityBuffer.getStr(), nPos ); cChar = GetHTMLCharName( sEntity ); // not found ( == 0 ): plain text // or a character which is inserted as attribute if( 0U == cChar && ';' != nNextCh ) { DBG_ASSERT( rInput.Tell() - nStreamPos == (sal_uLong)(nPos+1L)*GetCharSize(), "UTF-8 is failing here" ); for( xub_StrLen i=nPos-1L; i>1L; i-- ) { nNextCh = sEntityBuffer[i]; sEntityBuffer.setLength( i ); sEntity.Assign( sEntityBuffer.getStr(), i ); cChar = GetHTMLCharName( sEntity ); if( cChar ) { rInput.SeekRel( -(long) ((nPos-i)*GetCharSize()) ); nlLinePos -= sal_uInt32(nPos-i); nPos = i; ClearTxtConvContext(); break; } } } if( !cChar ) // unknown character? { // back in stream, insert '&' // and restart with next character sTmpBuffer.append( (sal_Unicode)'&' ); DBG_ASSERT( rInput.Tell()-nStreamPos == (sal_uLong)(nPos+1)*GetCharSize(), "Wrong stream position" ); DBG_ASSERT( nlLinePos-nLinePos == (sal_uLong)(nPos+1), "Wrong line position" ); rInput.Seek( nStreamPos ); nlLinePos = nLinePos; ClearTxtConvContext(); break; } // 1 == Non Breaking Space // 2 == SoftHyphen if( cChar < 3U ) { if( '>' == cBreak ) { // When reading the content of a tag we have // to change it to ' ' or '-' switch( cChar ) { case 1U: cChar = ' '; break; case 2U: cChar = '-'; break; default: DBG_ASSERT( cChar==1U, "\0x00 should be handled already!" ); break; } } else { // If not scanning a tag return token aToken += String( sTmpBuffer.makeStringAndClear() ); if( cChar ) { if( aToken.Len() ) { // restart with character nNextCh = '&'; DBG_ASSERT( rInput.Tell()-nStreamPos == (sal_uLong)(nPos+1)*GetCharSize(), "Wrong stream position" ); DBG_ASSERT( nlLinePos-nLinePos == (sal_uLong)(nPos+1), "Wrong line position" ); rInput.Seek( nStreamPos ); nlLinePos = nLinePos; ClearTxtConvContext(); return HTML_TEXTTOKEN; } // Hack: _GetNextChar shall not read the // next character if( ';' != nNextCh ) aToken += ' '; if( 1U == cChar ) return HTML_NONBREAKSPACE; if( 2U == cChar ) return HTML_SOFTHYPH; } aToken += (sal_Unicode)'&'; aToken += String(sEntityBuffer.makeStringAndClear()); break; } } } else nNextCh = 0U; } // &{...};-JavaScript-Macros are not supported any longer. else if( IsParserWorking() ) { sTmpBuffer.append( (sal_Unicode)'&' ); bNextCh = false; break; } bNextCh = (';' == nNextCh); if( cBreak=='>' && (cChar=='\\' || cChar=='\'' || cChar=='\"' || cChar==' ') ) { // ' and " have to be escaped withing tags to separate // them from ' and " enclosing options. // \ has to be escaped as well. // Space is protected because it's not a delimiter between // options. sTmpBuffer.append( (sal_Unicode)'\\' ); if( MAX_LEN == sTmpBuffer.getLength() ) aToken += String(sTmpBuffer.makeStringAndClear()); } if( IsParserWorking() ) { if( cChar ) sTmpBuffer.append( cChar ); } else if( SVPAR_PENDING==eState && '>'!=cBreak ) { // Restart with '&', the remainder is returned as // text token. if( aToken.Len() || sTmpBuffer.getLength() ) { // _GetNextChar() returns the previous text and // during the next execution a new character is read. // Thus we have to position in front of the '&'. nNextCh = 0U; rInput.Seek( nStreamPos-(sal_uInt32)GetCharSize() ); nlLinePos = nLinePos-1; ClearTxtConvContext(); bReadNextChar = true; } bNextCh = false; } } break; case '=': if( '>'==cBreak && !cQuote ) bEqSignFound = true; sTmpBuffer.append( nNextCh ); break; case '\\': if( '>'==cBreak ) { // Innerhalb von Tags kennzeichnen sTmpBuffer.append( (sal_Unicode)'\\' ); if( MAX_LEN == sTmpBuffer.getLength() ) aToken += String(sTmpBuffer.makeStringAndClear()); } sTmpBuffer.append( (sal_Unicode)'\\' ); break; case '\"': case '\'': if( '>'==cBreak ) { if( bEqSignFound ) cQuote = nNextCh; else if( cQuote && (cQuote==nNextCh ) ) cQuote = 0U; } sTmpBuffer.append( nNextCh ); bEqSignFound = false; break; case sal_Unicode(EOF): if( rInput.IsEof() ) { bContinue = false; } else { sTmpBuffer.append( nNextCh ); } break; case '<': bEqSignFound = false; if( '>'==cBreak ) sTmpBuffer.append( nNextCh ); else bContinue = false; // break, String zusammen break; case '\f': if( '>' == cBreak ) { // If scanning options treat it like a space, ... sTmpBuffer.append( (sal_Unicode)' ' ); } else { // otherwise it's a separate token. bContinue = false; } break; case '\r': case '\n': if( '>'==cBreak ) { // cr/lf in tag is handled in _GetNextToken() sTmpBuffer.append( nNextCh ); break; } else if( bReadListing || bReadXMP || bReadPRE || bReadTextArea ) { bContinue = false; break; } // Reduce sequence of CR/LF/BLANK/TAB to a single blank // no break!! case '\t': if( '\t'==nNextCh && bReadPRE && '>'!=cBreak ) { // In
: Tabs nach oben durchreichen
                bContinue = false;
                break;
            }
            // no break
        case '\x0b':
            if( '\x0b'==nNextCh && (bReadPRE || bReadXMP ||bReadListing) &&
                '>'!=cBreak )
            {
                break;
            }
            nNextCh = ' ';
            // no break;
        case ' ':
            sTmpBuffer.append( nNextCh );
            if( '>'!=cBreak && (!bReadListing && !bReadXMP &&
                                !bReadPRE && !bReadTextArea) )
            {
                // Reduce sequences of Blanks/Tabs/CR/LF to a single blank
                do {
                    if( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
                        rInput.IsEof() )
                    {
                        if( aToken.Len() || sTmpBuffer.getLength() > 1L )
                        {
                            // Have seen s.th. aside from blanks?
                            aToken += String(sTmpBuffer.makeStringAndClear());
                            return HTML_TEXTTOKEN;
                        }
                        else
                            // Only read blanks: no text must be returned
                            // and _GetNextToken has to read until EOF
                            return 0;
                    }
                } while ( ' ' == nNextCh || '\t' == nNextCh ||
                          '\r' == nNextCh || '\n' == nNextCh ||
                          '\x0b' == nNextCh );
                bNextCh = false;
            }
            break;

        default:
            bEqSignFound = false;
            if( (nNextCh==cBreak && !cQuote) ||
                (sal_uLong(aToken.Len()) + MAX_LEN) > sal_uLong(STRING_MAXLEN & ~1 ))
                bContinue = false;
            else
            {
                do {
                    // All remaining characters make their way into the text.
                    sTmpBuffer.append( nNextCh );
                    if( MAX_LEN == sTmpBuffer.getLength() )
                    {
                        aToken += String(sTmpBuffer.makeStringAndClear());
                        if( (sal_uLong(aToken.Len()) + MAX_LEN) >
                                sal_uLong(STRING_MAXLEN & ~1 ) )
                        {
                            nNextCh = GetNextChar();
                            return HTML_TEXTTOKEN;
                        }
                    }
                    if( ( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
                          rInput.IsEof() ) ||
                        !IsParserWorking() )
                    {
                        if( sTmpBuffer.getLength() )
                            aToken += String(sTmpBuffer.makeStringAndClear());
                        return HTML_TEXTTOKEN;
                    }
                } while( HTML_ISALPHA( nNextCh ) || HTML_ISDIGIT( nNextCh ) );
                bNextCh = false;
            }
        }

        if( MAX_LEN == sTmpBuffer.getLength() )
            aToken += String(sTmpBuffer.makeStringAndClear());

        if( bContinue && bNextCh )
            nNextCh = GetNextChar();
    }

    if( sTmpBuffer.getLength() )
        aToken += String(sTmpBuffer.makeStringAndClear());

    return HTML_TEXTTOKEN;
}

int HTMLParser::_GetNextRawToken()
{
    ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );

    if( bEndTokenFound )
    {
        // During the last execution we already found the end token,
        // thus we don't have to search it again.
        bReadScript = false;
        bReadStyle = false;
        aEndToken.Erase();
        bEndTokenFound = false;

        return 0;
    }

    // Default return value: HTML_RAWDATA
    int bContinue = true;
    int nToken = HTML_RAWDATA;
    SaveState( 0 );
    while( bContinue && IsParserWorking() )
    {
        int bNextCh = true;
        switch( nNextCh )
        {
        case '<':
            {
                // Maybe we've reached the end.

                // Save what we have read previously...
                aToken += String(sTmpBuffer.makeStringAndClear());

                // and remember position in stream.
                sal_uLong nStreamPos = rInput.Tell();
                sal_uLong nLineNr = GetLineNr();
                sal_uLong nLinePos = GetLinePos();

                // Start of an end token?
                int bOffState = false;
                if( '/' == (nNextCh = GetNextChar()) )
                {
                    bOffState = true;
                    nNextCh = GetNextChar();
                }
                else if( '!' == nNextCh )
                {
                    sTmpBuffer.append( nNextCh );
                    nNextCh = GetNextChar();
                }

                // Read following letters
                while( (HTML_ISALPHA(nNextCh) || '-'==nNextCh) &&
                       IsParserWorking() && sTmpBuffer.getLength() < MAX_LEN )
                {
                    sTmpBuffer.append( nNextCh );
                    nNextCh = GetNextChar();
                }

                String aTok( sTmpBuffer.getStr(),
                             sal::static_int_cast< xub_StrLen >(
                                 sTmpBuffer.getLength()) );
                aTok.ToUpperAscii();
                bool bDone = false;
                if( bReadScript || aEndToken.Len() )
                {
                    if( !bReadComment )
                    {
                        if( aTok.CompareToAscii( OOO_STRING_SVTOOLS_HTML_comment, 3 )
                                == COMPARE_EQUAL )
                        {
                            bReadComment = true;
                        }
                        else
                        {
                            // A script has to end with "". But
                            // ">" is optional for security reasons
                            bDone = bOffState &&
                            COMPARE_EQUAL == ( bReadScript
                                ? aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_script)
                                : aTok.CompareTo(aEndToken) );
                        }
                    }
                    if( bReadComment && '>'==nNextCh && aTok.Len() >= 2 &&
                        aTok.Copy( aTok.Len()-2 ).EqualsAscii( "--" ) )
                    {
                        // End of comment of style 
                        bReadComment = false;
                    }
                }
                else
                {
                    // Style sheets can be closed by ,  or 
                    if( bOffState )
                        bDone = aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_style)
                                    == COMPARE_EQUAL ||
                                aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_head)
                                    == COMPARE_EQUAL;
                    else
                        bDone =
                            aTok.CompareToAscii(OOO_STRING_SVTOOLS_HTML_body) == COMPARE_EQUAL;
                }

                if( bDone )
                {
                    // Done! Return the previously read string (if requested)
                    // and continue.

                    bContinue = false;

                    // nToken==0 means, _GetNextToken continues to read
                    if( !aToken.Len() && (bReadStyle || bReadScript) )
                    {
                        // Immediately close environment (or context?)
                        // and parse the end token
                        bReadScript = false;
                        bReadStyle = false;
                        aEndToken.Erase();
                        nToken = 0;
                    }
                    else
                    {
                        // Keep bReadScript/bReadStyle alive
                        // and parse end token during next execution
                        bEndTokenFound = true;
                    }

                    // Move backwards in stream to '<'
                    rInput.Seek( nStreamPos );
                    SetLineNr( nLineNr );
                    SetLinePos( nLinePos );
                    ClearTxtConvContext();
                    nNextCh = '<';

                    // Don't append string to token.
                    sTmpBuffer.setLength( 0L );
                }
                else
                {
                    // remember "' == nNextCh && IsParserWorking() && bTwoMinus )
                    bReadComment = false;

                bNextCh = false;
            }
            break;

        case '\r':
            // \r\n? closes the current text token (even if it's empty)
            nNextCh = GetNextChar();
            if( nNextCh=='\n' )
                nNextCh = GetNextChar();
            bContinue = false;
            break;
        case '\n':
            // \n closes the current text token (even if it's empty)
            nNextCh = GetNextChar();
            bContinue = false;
            break;
        case sal_Unicode(EOF):
            // eof closes the current text token and behaves like having read
            // an end token
            if( rInput.IsEof() )
            {
                bContinue = false;
                if( aToken.Len() || sTmpBuffer.getLength() )
                {
                    bEndTokenFound = true;
                }
                else
                {
                    bReadScript = false;
                    bReadStyle = false;
                    aEndToken.Erase();
                    nToken = 0;
                }
                break;
            }
            // no break
        default:
            // all remaining characters are appended to the buffer
            sTmpBuffer.append( nNextCh );
            break;
        }

        if( (!bContinue && sTmpBuffer.getLength() > 0L) ||
            MAX_LEN == sTmpBuffer.getLength() )
            aToken += String(sTmpBuffer.makeStringAndClear());

        if( bContinue && bNextCh )
            nNextCh = GetNextChar();
    }

    if( IsParserWorking() )
        SaveState( 0 );
    else
        nToken = 0;

    return nToken;
}

// Scan next token
int HTMLParser::_GetNextToken()
{
    int nRet = 0;
    sSaveToken.Erase();

    // Delete options
    if (!maOptions.empty())
        maOptions.clear();

    if( !IsParserWorking() )        // Don't continue if already an error occured
        return 0;

    bool bReadNextCharSave = bReadNextChar;
    if( bReadNextChar )
    {
        DBG_ASSERT( !bEndTokenFound,
                    "Read a character despite  was read?" );
        nNextCh = GetNextChar();
        if( !IsParserWorking() )        // Don't continue if already an error occured
            return 0;
        bReadNextChar = false;
    }

    if( bReadScript || bReadStyle || aEndToken.Len() )
    {
        nRet = _GetNextRawToken();
        if( nRet || !IsParserWorking() )
            return nRet;
    }

    do {
        int bNextCh = true;
        switch( nNextCh )
        {
        case '<':
            {
                sal_uLong nStreamPos = rInput.Tell();
                sal_uLong nLineNr = GetLineNr();
                sal_uLong nLinePos = GetLinePos();

                int bOffState = false;
                if( '/' == (nNextCh = GetNextChar()) )
                {
                    bOffState = true;
                    nNextCh = GetNextChar();
                }
                if( HTML_ISALPHA( nNextCh ) || '!'==nNextCh )
                {
                    ::rtl::OUStringBuffer sTmpBuffer;
                    do {
                        sTmpBuffer.append( nNextCh );
                        if( MAX_LEN == sTmpBuffer.getLength() )
                            aToken += String(sTmpBuffer.makeStringAndClear());
                        nNextCh = GetNextChar();
                    } while( '>' != nNextCh && !HTML_ISSPACE( nNextCh ) &&
                             IsParserWorking() && !rInput.IsEof() );

                    if( sTmpBuffer.getLength() )
                        aToken += String(sTmpBuffer.makeStringAndClear());

                    // Skip blanks
                    while( HTML_ISSPACE( nNextCh ) && IsParserWorking() )
                        nNextCh = GetNextChar();

                    if( !IsParserWorking() )
                    {
                        if( SVPAR_PENDING == eState )
                            bReadNextChar = bReadNextCharSave;
                        break;
                    }

                    // Search token in table:
                    sSaveToken = aToken;
                    aToken.ToUpperAscii();
                    if( 0 == (nRet = GetHTMLToken( aToken )) )
                        // Unknown control
                        nRet = HTML_UNKNOWNCONTROL_ON;

                    // If it's a token which can be switched off...
                    if( bOffState )
                    {
                         if( HTML_TOKEN_ONOFF & nRet )
                         {
                            // and there is an off token, return off token instead
                            ++nRet;
                         }
                         else if( HTML_LINEBREAK!=nRet )
                         {
                            // and there is no off token, return unknown token.
                            // (except for 
, that is treated like
) nRet = HTML_UNKNOWNCONTROL_OFF; } } if( nRet == HTML_COMMENT ) { // fix: due to being case sensitive use sSaveToken as start of comment // and append a blank. aToken = sSaveToken; if( '>'!=nNextCh ) aToken += (sal_Unicode)' '; sal_uLong nCStreamPos = 0; sal_uLong nCLineNr = 0; sal_uLong nCLinePos = 0; xub_StrLen nCStrLen = 0; bool bDone = false; // Read until closing -->. If not found restart at first > while( !bDone && !rInput.IsEof() && IsParserWorking() ) { if( '>'==nNextCh ) { if( !nCStreamPos ) { nCStreamPos = rInput.Tell(); nCStrLen = aToken.Len(); nCLineNr = GetLineNr(); nCLinePos = GetLinePos(); } bDone = aToken.Len() >= 2 && aToken.Copy(aToken.Len()-2,2). EqualsAscii( "--" ); if( !bDone ) aToken += nNextCh; } else aToken += nNextCh; if( !bDone ) nNextCh = GetNextChar(); } if( !bDone && IsParserWorking() && nCStreamPos ) { rInput.Seek( nCStreamPos ); SetLineNr( nCLineNr ); SetLinePos( nCLinePos ); ClearTxtConvContext(); aToken.Erase( nCStrLen ); nNextCh = '>'; } } else { // TokenString not needed anymore aToken.Erase(); } // Read until closing '>' if( '>' != nNextCh && IsParserWorking() ) { ScanText( '>' ); // fdo#34666 fdo#36080 fdo#36390: closing "/>"?: // return HTML__OFF instead of HTML__ON if ((HTML_TOKEN_ONOFF & nRet) && (aToken.Len() >= 1) && ('/' == aToken.GetChar(aToken.Len()-1))) { ++nRet; // HTML__ON -> HTML__OFF; } if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() ) { // Move back in front of < and restart there. // Return < as text. rInput.Seek( nStreamPos ); SetLineNr( nLineNr ); SetLinePos( nLinePos ); ClearTxtConvContext(); aToken = '<'; nRet = HTML_TEXTTOKEN; nNextCh = GetNextChar(); bNextCh = false; break; } } if( SVPAR_PENDING == eState ) bReadNextChar = bReadNextCharSave; } else { if( bOffState ) { // einfach alles wegschmeissen ScanText( '>' ); if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() ) { // Move back in front of < and restart there. // Return < as text. rInput.Seek( nStreamPos ); SetLineNr( nLineNr ); SetLinePos( nLinePos ); ClearTxtConvContext(); aToken = '<'; nRet = HTML_TEXTTOKEN; nNextCh = GetNextChar(); bNextCh = false; break; } if( SVPAR_PENDING == eState ) bReadNextChar = bReadNextCharSave; aToken.Erase(); } else if( '%' == nNextCh ) { nRet = HTML_UNKNOWNCONTROL_ON; sal_uLong nCStreamPos = rInput.Tell(); sal_uLong nCLineNr = GetLineNr(), nCLinePos = GetLinePos(); bool bDone = false; // Read until closing %>. If not found restart at first >. while( !bDone && !rInput.IsEof() && IsParserWorking() ) { bDone = '>'==nNextCh && aToken.Len() >= 1 && '%' == aToken.GetChar( aToken.Len()-1 ); if( !bDone ) { aToken += nNextCh; nNextCh = GetNextChar(); } } if( !bDone && IsParserWorking() ) { rInput.Seek( nCStreamPos ); SetLineNr( nCLineNr ); SetLinePos( nCLinePos ); ClearTxtConvContext(); aToken.AssignAscii( "<%", 2 ); nRet = HTML_TEXTTOKEN; break; } if( IsParserWorking() ) { sSaveToken = aToken; aToken.Erase(); } } else { aToken = '<'; nRet = HTML_TEXTTOKEN; bNextCh = false; break; } } if( IsParserWorking() ) { bNextCh = '>' == nNextCh; switch( nRet ) { case HTML_TEXTAREA_ON: bReadTextArea = true; break; case HTML_TEXTAREA_OFF: bReadTextArea = false; break; case HTML_SCRIPT_ON: if( !bReadTextArea ) bReadScript = true; break; case HTML_SCRIPT_OFF: if( !bReadTextArea ) { bReadScript = false; // JavaScript might modify the stream, // thus the last character has to be read again. bReadNextChar = true; bNextCh = false; } break; case HTML_STYLE_ON: bReadStyle = true; break; case HTML_STYLE_OFF: bReadStyle = false; break; } } } break; case sal_Unicode(EOF): if( rInput.IsEof() ) { eState = SVPAR_ACCEPTED; nRet = nNextCh; } else { // Read normal text. goto scan_text; } break; case '\f': // form feeds are passed upwards separately nRet = HTML_LINEFEEDCHAR; // !!! should be FORMFEEDCHAR break; case '\n': case '\r': if( bReadListing || bReadXMP || bReadPRE || bReadTextArea ) { sal_Unicode c = GetNextChar(); if( ( '\n' != nNextCh || '\r' != c ) && ( '\r' != nNextCh || '\n' != c ) ) { bNextCh = false; nNextCh = c; } nRet = HTML_NEWPARA; break; } // no break ! case '\t': if( bReadPRE ) { nRet = HTML_TABCHAR; break; } // no break ! case ' ': // no break ! default: scan_text: // "normal" text to come nRet = ScanText(); bNextCh = 0 == aToken.Len(); // the text should be processed if( !bNextCh && eState == SVPAR_PENDING ) { eState = SVPAR_WORKING; bReadNextChar = true; } break; } if( bNextCh && SVPAR_WORKING == eState ) { nNextCh = GetNextChar(); if( SVPAR_PENDING == eState && nRet && HTML_TEXTTOKEN != nRet ) { bReadNextChar = true; eState = SVPAR_WORKING; } } } while( !nRet && SVPAR_WORKING == eState ); if( SVPAR_PENDING == eState ) nRet = -1; // s.th. invalid return nRet; } void HTMLParser::UnescapeToken() { xub_StrLen nPos=0; bool bEscape = false; while( nPos < aToken.Len() ) { bool bOldEscape = bEscape; bEscape = false; if( '\\'==aToken.GetChar(nPos) && !bOldEscape ) { aToken.Erase( nPos, 1 ); bEscape = true; } else { nPos++; } } } const HTMLOptions& HTMLParser::GetOptions( sal_uInt16 *pNoConvertToken ) const { // If the options for the current token have already been returned, // return them once again. if (!maOptions.empty()) return maOptions; xub_StrLen nPos = 0; while( nPos < aToken.Len() ) { // A letter? Option beginning here. if( HTML_ISALPHA( aToken.GetChar(nPos) ) ) { int nToken; String aValue; xub_StrLen nStt = nPos; sal_Unicode cChar = 0; // Actually only certain characters allowed. // Netscape only looks for "=" and white space (c.f. // Mozilla: PA_FetchRequestedNameValues in lipparse/pa_mdl.c) while( nPos < aToken.Len() && '=' != (cChar=aToken.GetChar(nPos)) && HTML_ISPRINTABLE(cChar) && !HTML_ISSPACE(cChar) ) nPos++; String sName( aToken.Copy( nStt, nPos-nStt ) ); // PlugIns require original token name. Convert to upper case only for searching. String sNameUpperCase( sName ); sNameUpperCase.ToUpperAscii(); nToken = GetHTMLOption( sNameUpperCase ); // Name is ready DBG_ASSERTWARNING( nToken!=HTML_O_UNKNOWN, "GetOption: unknown HTML option" ); bool bStripCRLF = (nToken < HTML_OPTION_SCRIPT_START || nToken >= HTML_OPTION_SCRIPT_END) && (!pNoConvertToken || nToken != *pNoConvertToken); while( nPos < aToken.Len() && ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) || HTML_ISSPACE(cChar) ) ) nPos++; // Option with value? if( nPos!=aToken.Len() && '='==cChar ) { nPos++; while( nPos < aToken.Len() && ( !HTML_ISPRINTABLE( (cChar=aToken.GetChar(nPos)) ) || ' '==cChar || '\t'==cChar || '\r'==cChar || '\n'==cChar ) ) nPos++; if( nPos != aToken.Len() ) { xub_StrLen nLen = 0; nStt = nPos; if( ('"'==cChar) || ('\'')==cChar ) { sal_Unicode cEnd = cChar; nPos++; nStt++; bool bDone = false; bool bEscape = false; while( nPos < aToken.Len() && !bDone ) { bool bOldEscape = bEscape; bEscape = false; cChar = aToken.GetChar(nPos); switch( cChar ) { case '\r': case '\n': if( bStripCRLF ) ((String &)aToken).Erase( nPos, 1 ); else nPos++, nLen++; break; case '\\': if( bOldEscape ) { nPos++, nLen++; } else { ((String &)aToken).Erase( nPos, 1 ); bEscape = true; } break; case '"': case '\'': bDone = !bOldEscape && cChar==cEnd; if( !bDone ) nPos++, nLen++; break; default: nPos++, nLen++; break; } } if( nPos!=aToken.Len() ) nPos++; } else { // More liberal than the standard: allow all printable characters bool bEscape = false; bool bDone = false; while( nPos < aToken.Len() && !bDone ) { bool bOldEscape = bEscape; bEscape = false; sal_Unicode c = aToken.GetChar(nPos); switch( c ) { case ' ': bDone = !bOldEscape; if( !bDone ) nPos++, nLen++; break; case '\t': case '\r': case '\n': bDone = true; break; case '\\': if( bOldEscape ) { nPos++, nLen++; } else { ((String &)aToken).Erase( nPos, 1 ); bEscape = true; } break; default: if( HTML_ISPRINTABLE( c ) ) nPos++, nLen++; else bDone = true; break; } } } if( nLen ) aValue = aToken.Copy( nStt, nLen ); } } // Token is known and can be saved std::auto_ptr pOption( new HTMLOption(sal::static_int_cast(nToken), sName, aValue)); maOptions.push_back(pOption); } else // Ignore white space and unexpected characters nPos++; } return maOptions; } int HTMLParser::FilterPRE( int nToken ) { switch( nToken ) { #ifdef HTML_BEHAVIOUR // These become LFs according to the definition case HTML_PARABREAK_ON: case HTML_LINEBREAK: nToken = HTML_NEWPARA; #else // in Netscape they only have impact in not empty paragraphs case HTML_PARABREAK_ON: nToken = HTML_LINEBREAK; case HTML_LINEBREAK: #endif case HTML_NEWPARA: nPre_LinePos = 0; if( bPre_IgnoreNewPara ) nToken = 0; break; case HTML_TABCHAR: { xub_StrLen nSpaces = sal::static_int_cast< xub_StrLen >( 8 - (nPre_LinePos % 8)); DBG_ASSERT( !aToken.Len(), "Why is the token not empty?" ); aToken.Expand( nSpaces, ' ' ); nPre_LinePos += nSpaces; nToken = HTML_TEXTTOKEN; } break; // Keep those case HTML_TEXTTOKEN: nPre_LinePos += aToken.Len(); break; case HTML_SELECT_ON: case HTML_SELECT_OFF: case HTML_BODY_ON: case HTML_FORM_ON: case HTML_FORM_OFF: case HTML_INPUT: case HTML_OPTION: case HTML_TEXTAREA_ON: case HTML_TEXTAREA_OFF: case HTML_IMAGE: case HTML_APPLET_ON: case HTML_APPLET_OFF: case HTML_PARAM: case HTML_EMBED: case HTML_HEAD1_ON: case HTML_HEAD1_OFF: case HTML_HEAD2_ON: case HTML_HEAD2_OFF: case HTML_HEAD3_ON: case HTML_HEAD3_OFF: case HTML_HEAD4_ON: case HTML_HEAD4_OFF: case HTML_HEAD5_ON: case HTML_HEAD5_OFF: case HTML_HEAD6_ON: case HTML_HEAD6_OFF: case HTML_BLOCKQUOTE_ON: case HTML_BLOCKQUOTE_OFF: case HTML_ADDRESS_ON: case HTML_ADDRESS_OFF: case HTML_HORZRULE: case HTML_CENTER_ON: case HTML_CENTER_OFF: case HTML_DIVISION_ON: case HTML_DIVISION_OFF: case HTML_SCRIPT_ON: case HTML_SCRIPT_OFF: case HTML_RAWDATA: case HTML_TABLE_ON: case HTML_TABLE_OFF: case HTML_CAPTION_ON: case HTML_CAPTION_OFF: case HTML_COLGROUP_ON: case HTML_COLGROUP_OFF: case HTML_COL_ON: case HTML_COL_OFF: case HTML_THEAD_ON: case HTML_THEAD_OFF: case HTML_TFOOT_ON: case HTML_TFOOT_OFF: case HTML_TBODY_ON: case HTML_TBODY_OFF: case HTML_TABLEROW_ON: case HTML_TABLEROW_OFF: case HTML_TABLEDATA_ON: case HTML_TABLEDATA_OFF: case HTML_TABLEHEADER_ON: case HTML_TABLEHEADER_OFF: case HTML_ANCHOR_ON: case HTML_ANCHOR_OFF: case HTML_BOLD_ON: case HTML_BOLD_OFF: case HTML_ITALIC_ON: case HTML_ITALIC_OFF: case HTML_STRIKE_ON: case HTML_STRIKE_OFF: case HTML_STRIKETHROUGH_ON: case HTML_STRIKETHROUGH_OFF: case HTML_UNDERLINE_ON: case HTML_UNDERLINE_OFF: case HTML_BASEFONT_ON: case HTML_BASEFONT_OFF: case HTML_FONT_ON: case HTML_FONT_OFF: case HTML_BLINK_ON: case HTML_BLINK_OFF: case HTML_SPAN_ON: case HTML_SPAN_OFF: case HTML_SUBSCRIPT_ON: case HTML_SUBSCRIPT_OFF: case HTML_SUPERSCRIPT_ON: case HTML_SUPERSCRIPT_OFF: case HTML_BIGPRINT_ON: case HTML_BIGPRINT_OFF: case HTML_SMALLPRINT_OFF: case HTML_SMALLPRINT_ON: case HTML_EMPHASIS_ON: case HTML_EMPHASIS_OFF: case HTML_CITIATION_ON: case HTML_CITIATION_OFF: case HTML_STRONG_ON: case HTML_STRONG_OFF: case HTML_CODE_ON: case HTML_CODE_OFF: case HTML_SAMPLE_ON: case HTML_SAMPLE_OFF: case HTML_KEYBOARD_ON: case HTML_KEYBOARD_OFF: case HTML_VARIABLE_ON: case HTML_VARIABLE_OFF: case HTML_DEFINSTANCE_ON: case HTML_DEFINSTANCE_OFF: case HTML_SHORTQUOTE_ON: case HTML_SHORTQUOTE_OFF: case HTML_LANGUAGE_ON: case HTML_LANGUAGE_OFF: case HTML_AUTHOR_ON: case HTML_AUTHOR_OFF: case HTML_PERSON_ON: case HTML_PERSON_OFF: case HTML_ACRONYM_ON: case HTML_ACRONYM_OFF: case HTML_ABBREVIATION_ON: case HTML_ABBREVIATION_OFF: case HTML_INSERTEDTEXT_ON: case HTML_INSERTEDTEXT_OFF: case HTML_DELETEDTEXT_ON: case HTML_DELETEDTEXT_OFF: case HTML_TELETYPE_ON: case HTML_TELETYPE_OFF: break; // The remainder is treated as an unknown token. default: if( nToken ) { nToken = ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken)) ? HTML_UNKNOWNCONTROL_OFF : HTML_UNKNOWNCONTROL_ON ); } break; } bPre_IgnoreNewPara = false; return nToken; } int HTMLParser::FilterXMP( int nToken ) { switch( nToken ) { case HTML_NEWPARA: if( bPre_IgnoreNewPara ) nToken = 0; case HTML_TEXTTOKEN: case HTML_NONBREAKSPACE: case HTML_SOFTHYPH: break; // kept default: if( nToken ) { if( (HTML_TOKEN_ONOFF & nToken) && (1 & nToken) ) { sSaveToken.Insert( '<', 0 ); sSaveToken.Insert( '/', 1 ); } else sSaveToken.Insert( '<', 0 ); if( aToken.Len() ) { UnescapeToken(); sSaveToken += (sal_Unicode)' '; aToken.Insert( sSaveToken, 0 ); } else aToken = sSaveToken; aToken += (sal_Unicode)'>'; nToken = HTML_TEXTTOKEN; } break; } bPre_IgnoreNewPara = false; return nToken; } int HTMLParser::FilterListing( int nToken ) { switch( nToken ) { case HTML_NEWPARA: if( bPre_IgnoreNewPara ) nToken = 0; case HTML_TEXTTOKEN: case HTML_NONBREAKSPACE: case HTML_SOFTHYPH: break; // kept default: if( nToken ) { nToken = ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken)) ? HTML_UNKNOWNCONTROL_OFF : HTML_UNKNOWNCONTROL_ON ); } break; } bPre_IgnoreNewPara = false; return nToken; } bool HTMLParser::IsHTMLFormat( const sal_Char* pHeader, bool bSwitchToUCS2, rtl_TextEncoding eEnc ) { // If the string matches one of the following regular expressions then // the document is a HTML document. // // ^[^<]*<[^ \t]*[> \t] // ------- // ^' sal_Char c; sal_Int32 nPos; for( nPos = nStart; nPos < sCmp.getLength(); ++nPos ) { if( '>'==(c=sCmp[nPos]) || HTML_ISSPACE(c) ) break; } // If the document ends after < it's no HTML if( nPos==nStart ) return false; // the string following '<' has to be a known HTML token. // is not interpreted as HTML. Otherwise the output of the DOS command "DIR" // could be interpreted as HTML. String sTest( sCmp.copy( nStart, nPos-nStart ), RTL_TEXTENCODING_ASCII_US ); int nTok = GetHTMLToken( sTest ); if( 0 != nTok && HTML_DIRLIST_ON != nTok ) return true; // " somewhere in the first 80 characters of the document nStart = sCmp.indexOfL(RTL_CONSTASCII_STRINGPARAM(OOO_STRING_SVTOOLS_HTML_html)); if( nStart != -1 && nStart>0 && '<'==sCmp[nStart-1] && nStart+4 < sCmp.getLength() && '>'==sCmp[nStart+4] ) return true; // Else it's rather not a HTML document return false; } bool HTMLParser::InternalImgToPrivateURL( String& rURL ) { if( rURL.Len() < 19 || 'i' != rURL.GetChar(0) || rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher, 9 ) != COMPARE_EQUAL ) return false; bool bFound = false; if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_gopher,16) == COMPARE_EQUAL ) { String aName( rURL.Copy(16) ); switch( aName.GetChar(0) ) { case 'b': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_binary ); break; case 'i': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_image ) || aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_index ); break; case 'm': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_menu ) || aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_movie ); break; case 's': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_sound ); break; case 't': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_telnet ) || aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_text ); break; case 'u': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_GOPHER_unknown ); break; } } else if( rURL.CompareToAscii( OOO_STRING_SVTOOLS_HTML_internal_icon,14) == COMPARE_EQUAL ) { String aName( rURL.Copy(14) ); switch( aName.GetChar(0) ) { case 'b': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_baddata ); break; case 'd': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_delayed ); break; case 'e': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_embed ); break; case 'i': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_insecure ); break; case 'n': bFound = aName.EqualsAscii( OOO_STRING_SVTOOLS_HTML_INT_ICON_notfound ); break; } } if( bFound ) { String sTmp ( rURL ); rURL.AssignAscii( OOO_STRING_SVTOOLS_HTML_private_image ); rURL.Append( sTmp ); } return bFound; } enum eHtmlMetas { HTML_META_NONE = 0, HTML_META_AUTHOR, HTML_META_DESCRIPTION, HTML_META_KEYWORDS, HTML_META_REFRESH, HTML_META_CLASSIFICATION, HTML_META_CREATED, HTML_META_CHANGEDBY, HTML_META_CHANGED, HTML_META_GENERATOR, HTML_META_SDFOOTNOTE, HTML_META_SDENDNOTE, HTML_META_CONTENT_TYPE }; // static HTMLOptionEnum const aHTMLMetaNameTable[] = { { OOO_STRING_SVTOOLS_HTML_META_author, HTML_META_AUTHOR }, { OOO_STRING_SVTOOLS_HTML_META_changed, HTML_META_CHANGED }, { OOO_STRING_SVTOOLS_HTML_META_changedby, HTML_META_CHANGEDBY }, { OOO_STRING_SVTOOLS_HTML_META_classification,HTML_META_CLASSIFICATION}, { OOO_STRING_SVTOOLS_HTML_META_content_type, HTML_META_CONTENT_TYPE }, { OOO_STRING_SVTOOLS_HTML_META_created, HTML_META_CREATED }, { OOO_STRING_SVTOOLS_HTML_META_description, HTML_META_DESCRIPTION }, { OOO_STRING_SVTOOLS_HTML_META_keywords, HTML_META_KEYWORDS }, { OOO_STRING_SVTOOLS_HTML_META_generator, HTML_META_GENERATOR }, { OOO_STRING_SVTOOLS_HTML_META_refresh, HTML_META_REFRESH }, { OOO_STRING_SVTOOLS_HTML_META_sdendnote, HTML_META_SDENDNOTE }, { OOO_STRING_SVTOOLS_HTML_META_sdfootnote, HTML_META_SDFOOTNOTE }, { 0, 0 } }; void HTMLParser::AddMetaUserDefined( ::rtl::OUString const & ) { } bool HTMLParser::ParseMetaOptionsImpl( const uno::Reference & i_xDocProps, SvKeyValueIterator *i_pHTTPHeader, const HTMLOptions& aOptions, rtl_TextEncoding& o_rEnc ) { String aName, aContent; sal_uInt16 nAction = HTML_META_NONE; bool bHTTPEquiv = false, bChanged = false; for ( size_t i = aOptions.size(); i; ) { const HTMLOption& aOption = aOptions[--i]; switch ( aOption.GetToken() ) { case HTML_O_NAME: aName = aOption.GetString(); if ( HTML_META_NONE==nAction ) { aOption.GetEnum( nAction, aHTMLMetaNameTable ); } break; case HTML_O_HTTPEQUIV: aName = aOption.GetString(); aOption.GetEnum( nAction, aHTMLMetaNameTable ); bHTTPEquiv = true; break; case HTML_O_CONTENT: aContent = aOption.GetString(); break; } } if ( bHTTPEquiv || HTML_META_DESCRIPTION != nAction ) { // if it is not a Description, remove CRs and LFs from CONTENT aContent = comphelper::string::remove(aContent, _CR); aContent = comphelper::string::remove(aContent, _LF); } else { // convert line endings for Description aContent = convertLineEnd(aContent, GetSystemLineEnd()); } if ( bHTTPEquiv && i_pHTTPHeader ) { // Netscape seems to just ignore a closing ", so we do too if ( aContent.Len() && '"' == aContent.GetChar( aContent.Len()-1 ) ) { aContent.Erase( aContent.Len() - 1 ); } SvKeyValue aKeyValue( aName, aContent ); i_pHTTPHeader->Append( aKeyValue ); } switch ( nAction ) { case HTML_META_AUTHOR: if (i_xDocProps.is()) { i_xDocProps->setAuthor( aContent ); bChanged = true; } break; case HTML_META_DESCRIPTION: if (i_xDocProps.is()) { i_xDocProps->setDescription( aContent ); bChanged = true; } break; case HTML_META_KEYWORDS: if (i_xDocProps.is()) { i_xDocProps->setKeywords( ::comphelper::string::convertCommaSeparated(aContent)); bChanged = true; } break; case HTML_META_CLASSIFICATION: if (i_xDocProps.is()) { i_xDocProps->setSubject( aContent ); bChanged = true; } break; case HTML_META_CHANGEDBY: if (i_xDocProps.is()) { i_xDocProps->setModifiedBy( aContent ); } break; case HTML_META_CREATED: case HTML_META_CHANGED: if ( i_xDocProps.is() && aContent.Len() && comphelper::string::getTokenCount(aContent, ';') == 2 ) { Date aDate( (sal_uLong)aContent.GetToken(0).ToInt32() ); Time aTime( (sal_uLong)aContent.GetToken(1).ToInt32() ); DateTime aDateTime( aDate, aTime ); ::util::DateTime uDT(aDateTime.Get100Sec(), aDateTime.GetSec(), aDateTime.GetMin(), aDateTime.GetHour(), aDateTime.GetDay(), aDateTime.GetMonth(), aDateTime.GetYear()); if ( HTML_META_CREATED==nAction ) i_xDocProps->setCreationDate( uDT ); else i_xDocProps->setModificationDate( uDT ); bChanged = true; } break; case HTML_META_REFRESH: DBG_ASSERT( !bHTTPEquiv || i_pHTTPHeader, "Reload-URL aufgrund unterlassener MUSS-Aenderung verlorengegangen" ); break; case HTML_META_CONTENT_TYPE: if ( aContent.Len() ) { o_rEnc = GetEncodingByMIME( aContent ); } break; case HTML_META_NONE: if ( !bHTTPEquiv ) { if (i_xDocProps.is()) { uno::Reference xUDProps = i_xDocProps->getUserDefinedProperties(); try { xUDProps->addProperty(aName, beans::PropertyAttribute::REMOVEABLE, uno::makeAny(::rtl::OUString(aContent))); AddMetaUserDefined(aName); bChanged = true; } catch (uno::Exception &) { // ignore } } } break; default: break; } return bChanged; } bool HTMLParser::ParseMetaOptions( const uno::Reference & i_xDocProps, SvKeyValueIterator *i_pHeader ) { sal_uInt16 nContentOption = HTML_O_CONTENT; rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW; bool bRet = ParseMetaOptionsImpl( i_xDocProps, i_pHeader, GetOptions(&nContentOption), eEnc ); // If the encoding is set by a META tag, it may only overwrite the // current encoding if both, the current and the new encoding, are 1-sal_uInt8 // encodings. Everything else cannot lead to reasonable results. if (RTL_TEXTENCODING_DONTKNOW != eEnc && rtl_isOctetTextEncoding( eEnc ) && rtl_isOctetTextEncoding( GetSrcEncoding() ) ) { eEnc = GetExtendedCompatibilityTextEncoding( eEnc ); SetSrcEncoding( eEnc ); } return bRet; } rtl_TextEncoding HTMLParser::GetEncodingByMIME( const String& rMime ) { String sType; String sSubType; INetContentTypeParameterList aParameters; if (INetContentTypes::parse(rMime, sType, sSubType, &aParameters)) { const INetContentTypeParameter * pCharset = aParameters.find("charset"); if (pCharset != 0) { rtl::OString sValue(rtl::OUStringToOString(pCharset->m_sValue, RTL_TEXTENCODING_ASCII_US)); return GetExtendedCompatibilityTextEncoding( rtl_getTextEncodingFromMimeCharset( sValue.getStr() ) ); } } return RTL_TEXTENCODING_DONTKNOW; } rtl_TextEncoding HTMLParser::GetEncodingByHttpHeader( SvKeyValueIterator *pHTTPHeader ) { rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW; if( pHTTPHeader ) { SvKeyValue aKV; for( bool bCont = pHTTPHeader->GetFirst( aKV ); bCont; bCont = pHTTPHeader->GetNext( aKV ) ) { if( aKV.GetKey().EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_META_content_type ) ) { if( aKV.GetValue().Len() ) { eRet = HTMLParser::GetEncodingByMIME( aKV.GetValue() ); } } } } return eRet; } bool HTMLParser::SetEncodingByHTTPHeader( SvKeyValueIterator *pHTTPHeader ) { bool bRet = false; rtl_TextEncoding eEnc = HTMLParser::GetEncodingByHttpHeader( pHTTPHeader ); if(RTL_TEXTENCODING_DONTKNOW != eEnc) { SetSrcEncoding( eEnc ); bRet = true; } return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */