blob: ac3c0b0cebce194d786ad1e0a12987989d66bc72 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svtools.hxx"
#include <ctype.h>
#include <stdio.h>
#include <tools/stream.hxx>
#include <tools/debug.hxx>
#include <tools/color.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/strbuf.hxx>
#ifndef _SVSTDARR_HXX
#define _SVSTDARR_ULONGS
#include <svl/svstdarr.hxx>
#endif
#include <tools/tenccvt.hxx>
#include <tools/datetime.hxx>
#include <svl/inettype.hxx>
#include <comphelper/string.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <svtools/parhtml.hxx>
#include <svtools/htmltokn.h>
#include <svtools/htmlkywd.hxx>
using namespace ::com::sun::star;
const sal_Int32 MAX_LEN( 1024L );
//static sal_Unicode sTmpBuffer[ MAX_LEN+1 ];
const sal_Int32 MAX_MACRO_LEN( 1024 );
const sal_Int32 MAX_ENTITY_LEN( 8L );
/* */
// Tabellen zum Umwandeln von Options-Werten in Strings
// <INPUT TYPE=xxx>
static HTMLOptionEnum __READONLY_DATA 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 }
};
// <TABLE FRAME=xxx>
static HTMLOptionEnum __READONLY_DATA 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 }
};
// <TABLE RULES=xxx>
static HTMLOptionEnum __READONLY_DATA 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 }
};
SV_IMPL_PTRARR(HTMLOptions,HTMLOptionPtr)
/* */
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;
}
sal_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_END,
"HTMLOption: unbekanntes Token" );
}
sal_uInt32 HTMLOption::GetNumber() const
{
DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START &&
nToken<HTML_OPTION_NUMBER_END) ||
(nToken>=HTML_OPTION_CONTEXT_START &&
nToken<HTML_OPTION_CONTEXT_END) ||
nToken==HTML_O_VALUE,
"GetNumber: Option ist nicht numerisch" );
String aTmp( aValue );
aTmp.EraseLeadingChars();
sal_Int32 nTmp = aTmp.ToInt32();
return nTmp >= 0 ? (sal_uInt32)nTmp : 0;
}
sal_Int32 HTMLOption::GetSNumber() const
{
DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START && nToken<HTML_OPTION_NUMBER_END) ||
(nToken>=HTML_OPTION_CONTEXT_START && nToken<HTML_OPTION_CONTEXT_END),
"GetSNumber: Option ist nicht numerisch" );
String aTmp( aValue );
aTmp.EraseLeadingChars();
return aTmp.ToInt32();
}
void HTMLOption::GetNumbers( SvULongs &rLongs, sal_Bool bSpaceDelim ) const
{
if( rLongs.Count() )
rLongs.Remove( 0, rLongs.Count() );
if( bSpaceDelim )
{
// das ist ein sehr stark vereinfachter Scanner. Er sucht einfach
// alle Tiffern aus dem String
sal_Bool bInNum = sal_False;
sal_uLong nNum = 0;
for( xub_StrLen i=0; i<aValue.Len(); i++ )
{
register sal_Unicode c = aValue.GetChar( i );
if( c>='0' && c<='9' )
{
nNum *= 10;
nNum += (c - '0');
bInNum = sal_True;
}
else if( bInNum )
{
rLongs.Insert( nNum, rLongs.Count() );
bInNum = sal_False;
nNum = 0;
}
}
if( bInNum )
{
rLongs.Insert( nNum, rLongs.Count() );
}
}
else
{
// hier wird auf die korrekte Trennung der Zahlen durch ',' geachtet
// und auch mal eine 0 eingefuegt
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() )
rLongs.Insert( sal_uLong(0), rLongs.Count() );
else
{
xub_StrLen nEnd = aValue.Search( (sal_Unicode)',', nPos );
if( STRING_NOTFOUND==nEnd )
{
sal_Int32 nTmp = aValue.Copy(nPos).ToInt32();
rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0,
rLongs.Count() );
nPos = aValue.Len();
}
else
{
sal_Int32 nTmp =
aValue.Copy(nPos,nEnd-nPos).ToInt32();
rLongs.Insert( nTmp >= 0 ? (sal_uInt32)nTmp : 0,
rLongs.Count() );
nPos = nEnd+1;
}
}
}
}
}
void HTMLOption::GetColor( Color& rColor ) const
{
DBG_ASSERT( (nToken>=HTML_OPTION_COLOR_START && nToken<HTML_OPTION_COLOR_END) || nToken==HTML_O_SIZE,
"GetColor: Option spezifiziert keine Farbe" );
String aTmp( aValue );
aTmp.ToUpperAscii();
sal_uLong nColor = ULONG_MAX;
if( '#'!=aTmp.GetChar( 0 ) )
nColor = GetHTMLColor( aTmp );
if( ULONG_MAX == nColor )
{
nColor = 0;
xub_StrLen nPos = 0;
for( sal_uInt32 i=0; i<6; i++ )
{
// MIB 26.06.97: Wie auch immer Netscape Farbwerte ermittelt,
// maximal drei Zeichen, die kleiner als '0' sind werden
// ignoriert. Bug #40901# stimmt damit. Mal schauen, was sich
// irgendwelche HTML-Autoren noch so einfallen lassen...
register sal_Unicode c = nPos<aTmp.Len() ? aTmp.GetChar( nPos++ )
: '0';
if( c < '0' )
{
c = nPos<aTmp.Len() ? aTmp.GetChar(nPos++) : '0';
if( c < '0' )
c = nPos<aTmp.Len() ? aTmp.GetChar(nPos++) : '0';
}
nColor *= 16;
if( c >= '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 nicht TYPE" );
return (HTMLInputType)GetEnum( aInputTypeOptEnums, HTML_IT_TEXT );
}
HTMLTableFrame HTMLOption::GetTableFrame() const
{
DBG_ASSERT( nToken==HTML_O_FRAME, "GetTableFrame: Option nicht FRAME" );
return (HTMLTableFrame)GetEnum( aTableFrameOptEnums, HTML_TF_VOID );
}
HTMLTableRules HTMLOption::GetTableRules() const
{
DBG_ASSERT( nToken==HTML_O_RULES, "GetTableRules: Option nicht RULES" );
return (HTMLTableRules)GetEnum( aTableRulesOptEnums, HTML_TR_NONE );
}
/* */
HTMLParser::HTMLParser( SvStream& rIn, int bReadNewDoc )
: SvParser( rIn )
{
bNewDoc = bReadNewDoc;
bReadListing = bReadXMP = bReadPRE = bReadTextArea =
bReadScript = bReadStyle =
bEndTokenFound = bIsInBody = bReadNextChar =
bReadComment = sal_False;
bIsInHeader = sal_True;
pOptions = new HTMLOptions;
//#i76649, default to UTF-8 for HTML unless we know differently
SetSrcEncoding(RTL_TEXTENCODING_UTF8);
}
HTMLParser::~HTMLParser()
{
if( pOptions && pOptions->Count() )
pOptions->DeleteAndDestroy( 0, pOptions->Count() );
delete pOptions;
}
SvParserState __EXPORT HTMLParser::CallParser()
{
eState = SVPAR_WORKING;
nNextCh = GetNextChar();
SaveState( 0 );
nPre_LinePos = 0;
bPre_IgnoreNewPara = sal_False;
AddRef();
Continue( 0 );
if( SVPAR_PENDING != eState )
ReleaseRef(); // dann brauchen wir den Parser nicht mehr!
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 ); // bis hierhin abgearbeitet,
// weiter mit neuem Token!
nToken = GetNextToken();
}
}
int HTMLParser::FilterToken( int nToken )
{
switch( nToken )
{
case sal_Unicode(EOF):
nToken = 0;
break; // nicht verschicken
case HTML_HEAD_OFF:
bIsInBody = sal_True;
case HTML_HEAD_ON:
bIsInHeader = HTML_HEAD_ON == nToken;
break;
case HTML_BODY_ON:
case HTML_FRAMESET_ON:
bIsInHeader = sal_False;
bIsInBody = HTML_BODY_ON == nToken;
break;
case HTML_BODY_OFF:
bIsInBody = bReadPRE = bReadListing = bReadXMP = sal_False;
break;
case HTML_HTML_OFF:
nToken = 0;
bReadPRE = bReadListing = bReadXMP = sal_False;
break; // HTML_ON wurde auch nicht verschickt !
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 ) (c >= '0' && c <= '9')
#define HTML_ISALPHA( c ) ( (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') )
#define HTML_ISALNUM( c ) ( HTML_ISALPHA(c) || HTML_ISDIGIT(c) )
#define HTML_ISSPACE( c ) ( ' ' == c || (c >= 0x09 && c <= 0x0d) )
#define HTML_ISPRINTABLE( c ) ( c >= 32 && c != 127)
// --> OD 2006-07-26 #138464#
#define HTML_ISHEXDIGIT( c ) ( HTML_ISDIGIT(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') )
// <--
int HTMLParser::ScanText( const sal_Unicode cBreak )
{
::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
int bWeiter = sal_True;
int bEqSignFound = sal_False;
sal_Unicode cQuote = 0U;
while( bWeiter && IsParserWorking() )
{
int bNextCh = sal_True;
switch( nNextCh )
{
case '&':
bEqSignFound = sal_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();
// --> OD 2006-07-26 #138464#
// consider hexadecimal digits
const sal_Bool bIsHex( 'x' == nNextCh );
const sal_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 )
{
sal_Unicode cOrig = cChar;
cChar = ByteString::ConvertToUnicode(
(sal_Char)cChar, eSrcEnc );
if( 0U == cChar )
{
// #73398#: If the character could not be
// converted, because a conversion is not
// available, do no conversion at all.
cChar = cOrig;
}
}
}
// <--
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 );
// nicht gefunden ( == 0 ), dann Klartext
// oder ein Zeichen das als Attribut eingefuegt
// wird
if( 0U == cChar && ';' != nNextCh )
{
DBG_ASSERT( rInput.Tell() - nStreamPos ==
(sal_uLong)(nPos+1L)*GetCharSize(),
"UTF-8 geht hier schief" );
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 ) // unbekanntes Zeichen?
{
// dann im Stream zurueck, das '&' als Zeichen
// einfuegen und mit dem nachfolgenden Zeichen
// wieder aufsetzen
sTmpBuffer.append( (sal_Unicode)'&' );
// rInput.SeekRel( -(long)(++nPos*GetCharSize()) );
// nlLinePos -= nPos;
DBG_ASSERT( rInput.Tell()-nStreamPos ==
(sal_uLong)(nPos+1)*GetCharSize(),
"Falsche Stream-Position" );
DBG_ASSERT( nlLinePos-nLinePos ==
(sal_uLong)(nPos+1),
"Falsche Zeilen-Position" );
rInput.Seek( nStreamPos );
nlLinePos = nLinePos;
ClearTxtConvContext();
break;
}
// 1 == Non Breaking Space
// 2 == SoftHyphen
if( cChar < 3U )
{
if( '>' == cBreak )
{
// Wenn der Inhalt eines Tags gelesen wird,
// muessen wir ein Space bzw. - daraus machen
switch( cChar )
{
case 1U: cChar = ' '; break;
case 2U: cChar = '-'; break;
default:
DBG_ASSERT( cChar==1U,
"\0x00 sollte doch schon laengt abgefangen sein!" );
break;
}
}
else
{
// Wenn kein Tag gescannt wird, enstprechendes
// Token zurueckgeben
aToken +=
String( sTmpBuffer.makeStringAndClear() );
if( cChar )
{
if( aToken.Len() )
{
// mit dem Zeichen wieder aufsetzen
nNextCh = '&';
// rInput.SeekRel( -(long)(++nPos*GetCharSize()) );
// nlLinePos -= nPos;
DBG_ASSERT( rInput.Tell()-nStreamPos ==
(sal_uLong)(nPos+1)*GetCharSize(),
"Falsche Stream-Position" );
DBG_ASSERT( nlLinePos-nLinePos ==
(sal_uLong)(nPos+1),
"Falsche Zeilen-Position" );
rInput.Seek( nStreamPos );
nlLinePos = nLinePos;
ClearTxtConvContext();
return HTML_TEXTTOKEN;
}
// Hack: _GetNextChar soll nicht das
// naechste Zeichen lesen
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;
}
// MIB 03/02/2000: &{...};-JavaScript-Macros are not
// supported any longer.
else if( IsParserWorking() )
{
sTmpBuffer.append( (sal_Unicode)'&' );
bNextCh = sal_False;
break;
}
bNextCh = (';' == nNextCh);
if( cBreak=='>' && (cChar=='\\' || cChar=='\'' ||
cChar=='\"' || cChar==' ') )
{
// ' und " mussen innerhalb von Tags mit einem
// gekennzeichnet werden, um sie von ' und " als Klammern
// um Optionen zu unterscheiden. Logischerweise muss
// deshalb auch ein \ gekeenzeichnet werden. Ausserdem
// schuetzen wir ein Space, weil es kein Trennzeichen
// zwischen Optionen ist.
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 )
{
// Mit dem '&' Zeichen wieder aufsetzen, der Rest
// wird als Texttoken zurueckgegeben.
if( aToken.Len() || sTmpBuffer.getLength() )
{
// Der bisherige Text wird von _GetNextChar()
// zurueckgegeben und beim naechsten Aufruf wird
// ein neues Zeichen gelesen. Also muessen wir uns
// noch vor das & stellen.
nNextCh = 0U;
rInput.Seek( nStreamPos-(sal_uInt32)GetCharSize() );
nlLinePos = nLinePos-1;
ClearTxtConvContext();
bReadNextChar = sal_True;
}
bNextCh = sal_False;
}
}
break;
case '=':
if( '>'==cBreak && !cQuote )
bEqSignFound = sal_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 = sal_False;
break;
case sal_Unicode(EOF):
if( rInput.IsEof() )
{
// MIB 20.11.98: Das macht hier keinen Sinn, oder doch: Zumindest wird
// abc&auml;<EOF> nicht angezeigt, also lassen wir das in Zukunft.
// if( '>' != cBreak )
// eState = SVPAR_ACCEPTED;
bWeiter = sal_False;
}
else
{
sTmpBuffer.append( nNextCh );
}
break;
case '<':
bEqSignFound = sal_False;
if( '>'==cBreak )
sTmpBuffer.append( nNextCh );
else
bWeiter = sal_False; // Abbrechen, String zusammen
break;
case '\f':
if( '>' == cBreak )
{
// Beim Scannen von Optionen wie ein Space behandeln
sTmpBuffer.append( (sal_Unicode)' ' );
}
else
{
// sonst wird es ein eigenes Token
bWeiter = sal_False;
}
break;
case '\r':
case '\n':
if( '>'==cBreak )
{
// #26979# cr/lf in Tag wird in _GetNextToken() behandeln
sTmpBuffer.append( nNextCh );
break;
}
else if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
{
bWeiter = sal_False;
break;
}
// Bug 18984: CR-LF -> Blank
// Folge von CR/LF/BLANK/TAB nur in ein Blank wandeln
// kein break!!
case '\t':
if( '\t'==nNextCh && bReadPRE && '>'!=cBreak )
{
// In <PRE>: Tabs nach oben durchreichen
bWeiter = sal_False;
break;
}
// kein break
case '\x0b':
if( '\x0b'==nNextCh && (bReadPRE || bReadXMP ||bReadListing) &&
'>'!=cBreak )
{
break;
}
nNextCh = ' ';
// kein break;
case ' ':
sTmpBuffer.append( nNextCh );
if( '>'!=cBreak && (!bReadListing && !bReadXMP &&
!bReadPRE && !bReadTextArea) )
{
// alle Folgen von Blanks/Tabs/CR/LF zu einem Blank umwandeln
do {
if( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
rInput.IsEof() )
{
if( aToken.Len() || sTmpBuffer.getLength() > 1L )
{
// ausser den Blanks wurde noch etwas geselen
aToken += String(sTmpBuffer.makeStringAndClear());
return HTML_TEXTTOKEN;
}
else
// nur Blanks gelesen: dann darf kein Text
// mehr zurueckgegeben werden und _GetNextToken
// muss auf EOF laufen
return 0;
}
} while ( ' ' == nNextCh || '\t' == nNextCh ||
'\r' == nNextCh || '\n' == nNextCh ||
'\x0b' == nNextCh );
bNextCh = sal_False;
}
break;
default:
bEqSignFound = sal_False;
if( (nNextCh==cBreak && !cQuote) ||
(sal_uLong(aToken.Len()) + MAX_LEN) > sal_uLong(STRING_MAXLEN & ~1 ))
bWeiter = sal_False;
else
{
do {
// alle anderen Zeichen kommen in den 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 = sal_False;
}
}
if( MAX_LEN == sTmpBuffer.getLength() )
aToken += String(sTmpBuffer.makeStringAndClear());
if( bWeiter && bNextCh )
nNextCh = GetNextChar();
}
if( sTmpBuffer.getLength() )
aToken += String(sTmpBuffer.makeStringAndClear());
return HTML_TEXTTOKEN;
}
int HTMLParser::_GetNextRawToken()
{
::rtl::OUStringBuffer sTmpBuffer( MAX_LEN );
if( bEndTokenFound )
{
// beim letzten Aufruf haben wir das End-Token bereits gefunden,
// deshalb muessen wir es nicht noch einmal suchen
bReadScript = sal_False;
bReadStyle = sal_False;
aEndToken.Erase();
bEndTokenFound = sal_False;
return 0;
}
// per default geben wir HTML_RAWDATA zurueck
int bWeiter = sal_True;
int nToken = HTML_RAWDATA;
SaveState( 0 );
while( bWeiter && IsParserWorking() )
{
int bNextCh = sal_True;
switch( nNextCh )
{
case '<':
{
// Vielleicht haben wir das Ende erreicht
// das bisher gelesene erstmal retten
aToken += String(sTmpBuffer.makeStringAndClear());
// und die Position im Stream merken
sal_uLong nStreamPos = rInput.Tell();
sal_uLong nLineNr = GetLineNr();
sal_uLong nLinePos = GetLinePos();
// Start eines End-Token?
int bOffState = sal_False;
if( '/' == (nNextCh = GetNextChar()) )
{
bOffState = sal_True;
nNextCh = GetNextChar();
}
else if( '!' == nNextCh )
{
sTmpBuffer.append( nNextCh );
nNextCh = GetNextChar();
}
// jetzt die Buchstaben danach lesen
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();
sal_Bool bDone = sal_False;
if( bReadScript || aEndToken.Len() )
{
if( !bReadComment )
{
if( aTok.CompareToAscii( OOO_STRING_SVTOOLS_HTML_comment, 3 )
== COMPARE_EQUAL )
{
bReadComment = sal_True;
}
else
{
// ein Script muss mit "</SCRIPT>" aufhoehren, wobei
// wir es mit dem ">" aus sicherheitsgruenden
// erstmal nicht so genau nehmen
bDone = bOffState && // '>'==nNextCh &&
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( "--" ) )
{
// hier ist ein Kommentar der Art <!-----> zuende
bReadComment = sal_False;
}
}
else
{
// ein Style-Sheet kann mit </STYLE>, </HEAD> oder
// <BODY> aughoehren
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 )
{
// das war's, jetzt muessen wir gegebenenfalls den
// bisher gelesenen String zurueckgeben und dnach normal
// weitermachen
bWeiter = sal_False;
// nToken==0 heisst, dass _GetNextToken gleich weiterliest
if( !aToken.Len() && (bReadStyle || bReadScript) )
{
// wir koennen sofort die Umgebung beeden und
// das End-Token parsen
bReadScript = sal_False;
bReadStyle = sal_False;
aEndToken.Erase();
nToken = 0;
}
else
{
// wir muessen bReadScript/bReadStyle noch am
// Leben lassen und koennen erst beim naechsten
// mal das End-Token Parsen
bEndTokenFound = sal_True;
}
// jetzt fahren wir im Stream auf das '<' zurueck
rInput.Seek( nStreamPos );
SetLineNr( nLineNr );
SetLinePos( nLinePos );
ClearTxtConvContext();
nNextCh = '<';
// den String wollen wir nicht an das Token haengen
sTmpBuffer.setLength( 0L );
}
else
{
// "</" merken, alles andere steht noch im buffer
aToken += (sal_Unicode)'<';
if( bOffState )
aToken += (sal_Unicode)'/';
bNextCh = sal_False;
}
}
break;
case '-':
sTmpBuffer.append( nNextCh );
if( bReadComment )
{
sal_Bool bTwoMinus = sal_False;
nNextCh = GetNextChar();
while( '-' == nNextCh && IsParserWorking() )
{
bTwoMinus = sal_True;
if( MAX_LEN == sTmpBuffer.getLength() )
aToken += String(sTmpBuffer.makeStringAndClear());
sTmpBuffer.append( nNextCh );
nNextCh = GetNextChar();
}
if( '>' == nNextCh && IsParserWorking() && bTwoMinus )
bReadComment = sal_False;
bNextCh = sal_False;
}
break;
case '\r':
// \r\n? beendet das aktuelle Text-Token (auch wenn es leer ist)
nNextCh = GetNextChar();
if( nNextCh=='\n' )
nNextCh = GetNextChar();
bWeiter = sal_False;
break;
case '\n':
// \n beendet das aktuelle Text-Token (auch wenn es leer ist)
nNextCh = GetNextChar();
bWeiter = sal_False;
break;
case sal_Unicode(EOF):
// eof beendet das aktuelle Text-Token und tut so, als ob
// ein End-Token gelesen wurde
if( rInput.IsEof() )
{
bWeiter = sal_False;
if( aToken.Len() || sTmpBuffer.getLength() )
{
bEndTokenFound = sal_True;
}
else
{
bReadScript = sal_False;
bReadStyle = sal_False;
aEndToken.Erase();
nToken = 0;
}
break;
}
// kein break
default:
// alle anderen Zeichen landen im Buffer
sTmpBuffer.append( nNextCh );
break;
}
if( (!bWeiter && sTmpBuffer.getLength() > 0L) ||
MAX_LEN == sTmpBuffer.getLength() )
aToken += String(sTmpBuffer.makeStringAndClear());
if( bWeiter && bNextCh )
nNextCh = GetNextChar();
}
if( IsParserWorking() )
SaveState( 0 );
else
nToken = 0;
return nToken;
}
// scanne das naechste Token,
int __EXPORT HTMLParser::_GetNextToken()
{
int nRet = 0;
sSaveToken.Erase();
// die Optionen loeschen
if( pOptions->Count() )
pOptions->DeleteAndDestroy( 0, pOptions->Count() );
if( !IsParserWorking() ) // wenn schon Fehler, dann nicht weiter!
return 0;
sal_Bool bReadNextCharSave = bReadNextChar;
if( bReadNextChar )
{
DBG_ASSERT( !bEndTokenFound,
"</SCRIPT> gelesen und trotzdem noch ein Zeichen lesen?" );
nNextCh = GetNextChar();
if( !IsParserWorking() ) // wenn schon Fehler, dann nicht weiter!
return 0;
bReadNextChar = sal_False;
}
if( bReadScript || bReadStyle || aEndToken.Len() )
{
nRet = _GetNextRawToken();
if( nRet || !IsParserWorking() )
return nRet;
}
do {
int bNextCh = sal_True;
switch( nNextCh )
{
case '<':
{
sal_uLong nStreamPos = rInput.Tell();
sal_uLong nLineNr = GetLineNr();
sal_uLong nLinePos = GetLinePos();
int bOffState = sal_False;
if( '/' == (nNextCh = GetNextChar()) )
{
bOffState = sal_True;
nNextCh = GetNextChar();
}
if( HTML_ISALPHA( nNextCh ) || '!'==nNextCh ) // fix #26984#
{
::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());
// Blanks ueberlesen
while( HTML_ISSPACE( nNextCh ) && IsParserWorking() )
nNextCh = GetNextChar();
if( !IsParserWorking() )
{
if( SVPAR_PENDING == eState )
bReadNextChar = bReadNextCharSave;
break;
}
// suche das Token in der Tabelle:
sSaveToken = aToken;
aToken.ToUpperAscii();
if( 0 == (nRet = GetHTMLToken( aToken )) )
// Unknown Control
nRet = HTML_UNKNOWNCONTROL_ON;
// Wenn es ein Token zum ausschalten ist ...
if( bOffState )
{
if( HTML_TOKEN_ONOFF & nRet )
{
// und es ein Off-Token gibt, das daraus machen
++nRet;
}
else if( HTML_LINEBREAK!=nRet )
{
// und es kein Off-Token gibt, ein unbekanntes
// Token daraus machen (ausser </BR>, das wird
// wie <BR> behandelt
nRet = HTML_UNKNOWNCONTROL_OFF;
}
}
if( nRet == HTML_COMMENT )
{
// fix: sSaveToken wegen Gross-/Kleinschreibung
// als Anfang des Kommentars benutzen und ein
// Space anhaengen.
aToken = sSaveToken;
if( '>'!=nNextCh )
aToken += (sal_Unicode)' ';
sal_uLong nCStreamPos = 0;
sal_uLong nCLineNr = 0;
sal_uLong nCLinePos = 0;
xub_StrLen nCStrLen = 0;
sal_Bool bDone = sal_False;
// bis zum schliessenden --> lesen. wenn keins gefunden
// wurde beim der ersten > wieder aufsetzen
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
{
// den TokenString koennen wir jetzt verwerfen
aToken.Erase();
}
// dann lesen wir mal alles bis zur schliessenden '>'
if( '>' != nNextCh && IsParserWorking() )
{
ScanText( '>' );
if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
{
// zurueck hinter die < gehen und dort neu
// aufsetzen, das < als Text zurueckgeben
rInput.Seek( nStreamPos );
SetLineNr( nLineNr );
SetLinePos( nLinePos );
ClearTxtConvContext();
aToken = '<';
nRet = HTML_TEXTTOKEN;
nNextCh = GetNextChar();
bNextCh = sal_False;
break;
}
}
if( SVPAR_PENDING == eState )
bReadNextChar = bReadNextCharSave;
}
else
{
if( bOffState )
{
// einfach alles wegschmeissen
ScanText( '>' );
if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
{
// zurueck hinter die < gehen und dort neu
// aufsetzen, das < als Text zurueckgeben
rInput.Seek( nStreamPos );
SetLineNr( nLineNr );
SetLinePos( nLinePos );
ClearTxtConvContext();
aToken = '<';
nRet = HTML_TEXTTOKEN;
nNextCh = GetNextChar();
bNextCh = sal_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();
sal_Bool bDone = sal_False;
// bis zum schliessenden %> lesen. wenn keins gefunden
// wurde beim der ersten > wieder aufsetzen
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 = sal_False;
break;
}
}
if( IsParserWorking() )
{
bNextCh = '>' == nNextCh;
switch( nRet )
{
case HTML_TEXTAREA_ON:
bReadTextArea = sal_True;
break;
case HTML_TEXTAREA_OFF:
bReadTextArea = sal_False;
break;
case HTML_SCRIPT_ON:
if( !bReadTextArea )
bReadScript = sal_True;
break;
case HTML_SCRIPT_OFF:
if( !bReadTextArea )
{
bReadScript = sal_False;
// JavaScript kann den Stream veraendern
// also muss das letzte Zeichen nochmals
// gelesen werden
bReadNextChar = sal_True;
bNextCh = sal_False;
}
break;
case HTML_STYLE_ON:
bReadStyle = sal_True;
break;
case HTML_STYLE_OFF:
bReadStyle = sal_False;
break;
}
}
}
break;
case sal_Unicode(EOF):
if( rInput.IsEof() )
{
eState = SVPAR_ACCEPTED;
nRet = nNextCh;
}
else
{
// normalen Text lesen
goto scan_text;
}
break;
case '\f':
// Form-Feeds werden jetzt extra nach oben gereicht
nRet = HTML_LINEFEEDCHAR; // !!! eigentlich 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 = sal_False;
nNextCh = c;
}
nRet = HTML_NEWPARA;
break;
}
// kein break !
case '\t':
if( bReadPRE )
{
nRet = HTML_TABCHAR;
break;
}
// kein break !
case ' ':
// kein break !
default:
scan_text:
// es folgt "normaler" Text
nRet = ScanText();
bNextCh = 0 == aToken.Len();
// der Text sollte noch verarbeitet werden
if( !bNextCh && eState == SVPAR_PENDING )
{
eState = SVPAR_WORKING;
bReadNextChar = sal_True;
}
break;
}
if( bNextCh && SVPAR_WORKING == eState )
{
nNextCh = GetNextChar();
if( SVPAR_PENDING == eState && nRet && HTML_TEXTTOKEN != nRet )
{
bReadNextChar = sal_True;
eState = SVPAR_WORKING;
}
}
} while( !nRet && SVPAR_WORKING == eState );
if( SVPAR_PENDING == eState )
nRet = -1; // irgendwas ungueltiges
return nRet;
}
void HTMLParser::UnescapeToken()
{
xub_StrLen nPos=0;
sal_Bool bEscape = sal_False;
while( nPos < aToken.Len() )
{
sal_Bool bOldEscape = bEscape;
bEscape = sal_False;
if( '\\'==aToken.GetChar(nPos) && !bOldEscape )
{
aToken.Erase( nPos, 1 );
bEscape = sal_True;
}
else
{
nPos++;
}
}
}
// hole die Optionen
const HTMLOptions *HTMLParser::GetOptions( sal_uInt16 *pNoConvertToken ) const
{
// wenn die Option fuer das aktuelle Token schon einmal
// geholt wurden, geben wir sie noch einmal zurueck
if( pOptions->Count() )
return pOptions;
xub_StrLen nPos = 0;
while( nPos < aToken.Len() )
{
// ein Zeichen ? Dann faengt hier eine Option an
if( HTML_ISALPHA( aToken.GetChar(nPos) ) )
{
int nToken;
String aValue;
xub_StrLen nStt = nPos;
sal_Unicode cChar = 0;
// Eigentlich sind hier nur ganz bestimmte Zeichen erlaubt.
// Netscape achtet aber nur auf "=" und Leerzeichen (siehe
// Mozilla: PA_FetchRequestedNameValues in
// lipparse/pa_mdl.c
// while( nPos < aToken.Len() &&
// ( '-'==(c=aToken[nPos]) || isalnum(c) || '.'==c || '_'==c) )
while( nPos < aToken.Len() && '=' != (cChar=aToken.GetChar(nPos)) &&
HTML_ISPRINTABLE(cChar) && !HTML_ISSPACE(cChar) )
nPos++;
String sName( aToken.Copy( nStt, nPos-nStt ) );
//JP 23.03.97: die PlugIns wollen die TokenName im "Original" haben
// also nur fuers Suchen in UpperCase wandeln
String sNameUpperCase( sName );
sNameUpperCase.ToUpperAscii();
nToken = GetHTMLOption( sNameUpperCase ); // der Name ist fertig
DBG_ASSERTWARNING( nToken!=HTML_O_UNKNOWN,
"GetOption: unbekannte HTML-Option" );
sal_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++;
// hat die Option auch einen Wert?
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++;
sal_Bool bDone = sal_False;
sal_Bool bEscape = sal_False;
while( nPos < aToken.Len() && !bDone )
{
sal_Bool bOldEscape = bEscape;
bEscape = sal_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 = sal_True;
}
break;
case '"':
case '\'':
bDone = !bOldEscape && cChar==cEnd;
if( !bDone )
nPos++, nLen++;
break;
default:
nPos++, nLen++;
break;
}
}
if( nPos!=aToken.Len() )
nPos++;
}
else
{
// hier sind wir etwas laxer als der
// Standard und erlauben alles druckbare
sal_Bool bEscape = sal_False;
sal_Bool bDone = sal_False;
while( nPos < aToken.Len() && !bDone )
{
sal_Bool bOldEscape = bEscape;
bEscape = sal_False;
sal_Unicode c = aToken.GetChar(nPos);
switch( c )
{
case ' ':
bDone = !bOldEscape;
if( !bDone )
nPos++, nLen++;
break;
case '\t':
case '\r':
case '\n':
bDone = sal_True;
break;
case '\\':
if( bOldEscape )
{
nPos++, nLen++;
}
else
{
((String &)aToken).Erase( nPos, 1 );
bEscape = sal_True;
}
break;
default:
if( HTML_ISPRINTABLE( c ) )
nPos++, nLen++;
else
bDone = sal_True;
break;
}
}
}
if( nLen )
aValue = aToken.Copy( nStt, nLen );
}
}
// Wir kennen das Token und koennen es Speichern
HTMLOption *pOption =
new HTMLOption(
sal::static_int_cast< sal_uInt16 >(nToken), sName, aValue );
pOptions->Insert( pOption, pOptions->Count() );
}
else
// white space un unerwartete Zeichen ignorieren wie
nPos++;
}
return pOptions;
}
int HTMLParser::FilterPRE( int nToken )
{
switch( nToken )
{
#ifdef HTML_BEHAVIOUR
// diese werden laut Definition zu LFs
case HTML_PARABREAK_ON:
case HTML_LINEBREAK:
nToken = HTML_NEWPARA;
#else
// in Netscape zeigen sie aber nur in nicht-leeren Absaetzen Wirkung
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(), "Wieso ist das Token nicht leer?" );
aToken.Expand( nSpaces, ' ' );
nPre_LinePos += nSpaces;
nToken = HTML_TEXTTOKEN;
}
break;
// diese bleiben erhalten
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;
// der Rest wird als unbekanntes Token behandelt
default:
if( nToken )
{
nToken =
( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
? HTML_UNKNOWNCONTROL_OFF
: HTML_UNKNOWNCONTROL_ON );
}
break;
}
bPre_IgnoreNewPara = sal_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; // bleiben erhalten
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 = sal_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; // bleiben erhalten
default:
if( nToken )
{
nToken =
( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
? HTML_UNKNOWNCONTROL_OFF
: HTML_UNKNOWNCONTROL_ON );
}
break;
}
bPre_IgnoreNewPara = sal_False;
return nToken;
}
FASTBOOL HTMLParser::IsHTMLFormat( const sal_Char* pHeader,
sal_Bool bSwitchToUCS2,
rtl_TextEncoding eEnc )
{
// Einer der folgenden regulaeren Ausdrucke muss sich auf den String
// anwenden lassen, damit das Dok ein HTML-Dokument ist.
//
// ^[^<]*<[^ \t]*[> \t]
// -------
// ^<!
//
// wobei der unterstrichene Teilausdruck einem HTML-Token
// ensprechen muss
ByteString sCmp;
sal_Bool bUCS2B = sal_False;
if( bSwitchToUCS2 )
{
if( 0xfeU == (sal_uChar)pHeader[0] &&
0xffU == (sal_uChar)pHeader[1] )
{
eEnc = RTL_TEXTENCODING_UCS2;
bUCS2B = sal_True;
}
else if( 0xffU == (sal_uChar)pHeader[0] &&
0xfeU == (sal_uChar)pHeader[1] )
{
eEnc = RTL_TEXTENCODING_UCS2;
}
}
if
(
RTL_TEXTENCODING_UCS2 == eEnc &&
(
(0xfe == (sal_uChar)pHeader[0] && 0xff == (sal_uChar)pHeader[1]) ||
(0xff == (sal_uChar)pHeader[0] && 0xfe == (sal_uChar)pHeader[1])
)
)
{
if( 0xfe == (sal_uChar)pHeader[0] )
bUCS2B = sal_True;
xub_StrLen nLen;
for( nLen = 2;
pHeader[nLen] != 0 || pHeader[nLen+1] != 0;
nLen+=2 )
;
::rtl::OStringBuffer sTmp( (nLen - 2)/2 );
for( xub_StrLen nPos = 2; nPos < nLen; nPos += 2 )
{
sal_Unicode cUC;
if( bUCS2B )
cUC = (sal_Unicode(pHeader[nPos]) << 8) | pHeader[nPos+1];
else
cUC = (sal_Unicode(pHeader[nPos+1]) << 8) | pHeader[nPos];
if( 0U == cUC )
break;
sTmp.append( cUC < 256U ? (sal_Char)cUC : '.' );
}
sCmp = ByteString( sTmp.makeStringAndClear() );
}
else
{
sCmp = (sal_Char *)pHeader;
}
sCmp.ToUpperAscii();
// Ein HTML-Dokument muss in der ersten Zeile ein '<' besitzen
xub_StrLen nStart = sCmp.Search( '<' );
if( STRING_NOTFOUND == nStart )
return sal_False;
nStart++;
// danach duerfen beliebige andere Zeichen bis zu einem blank oder
// '>' kommen
sal_Char c;
xub_StrLen nPos;
for( nPos = nStart; nPos<sCmp.Len(); nPos++ )
{
if( '>'==(c=sCmp.GetChar(nPos)) || HTML_ISSPACE(c) )
break;
}
// wenn das Dokeument hinter dem < aufhoert ist es wohl kein HTML
if( nPos==nStart )
return sal_False;
// die Zeichenkette nach dem '<' muss ausserdem ein bekanntes
// HTML Token sein. Damit die Ausgabe eines DOS-dir-Befehls nicht
// als HTML interpretiert wird, wird ein <DIR> jedoch nicht als HTML
// interpretiert.
String sTest( sCmp.Copy( nStart, nPos-nStart ), RTL_TEXTENCODING_ASCII_US );
int nTok = GetHTMLToken( sTest );
if( 0 != nTok && HTML_DIRLIST_ON != nTok )
return sal_True;
// oder es handelt sich um ein "<!" ganz am Anfang der Datei (fix #27092#)
if( nStart == 1 && '!' == sCmp.GetChar( 1 ) )
return sal_True;
// oder wir finden irgendwo ein <HTML> in den ersten 80 Zeichen
nStart = sCmp.Search( OOO_STRING_SVTOOLS_HTML_html );
if( nStart!=STRING_NOTFOUND &&
nStart>0 && '<'==sCmp.GetChar(nStart-1) &&
nStart+4 < sCmp.Len() && '>'==sCmp.GetChar(nStart+4) )
return sal_True;
// sonst ist es wohl doch eher kein HTML-Dokument
return sal_False;
}
sal_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 sal_False;
sal_Bool bFound = sal_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;
}
#ifdef USED
void HTMLParser::SaveState( int nToken )
{
SvParser::SaveState( nToken );
}
void HTMLParser::RestoreState()
{
SvParser::RestoreState();
}
#endif
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
};
// <META NAME=xxx>
static HTMLOptionEnum __READONLY_DATA 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<document::XDocumentProperties> & i_xDocProps,
SvKeyValueIterator *i_pHTTPHeader,
const HTMLOptions *i_pOptions,
rtl_TextEncoding& o_rEnc )
{
String aName, aContent;
sal_uInt16 nAction = HTML_META_NONE;
bool bHTTPEquiv = false, bChanged = false;
for ( sal_uInt16 i = i_pOptions->Count(); i; )
{
const HTMLOption *pOption = (*i_pOptions)[ --i ];
switch ( pOption->GetToken() )
{
case HTML_O_NAME:
aName = pOption->GetString();
if ( HTML_META_NONE==nAction )
{
pOption->GetEnum( nAction, aHTMLMetaNameTable );
}
break;
case HTML_O_HTTPEQUIV:
aName = pOption->GetString();
pOption->GetEnum( nAction, aHTMLMetaNameTable );
bHTTPEquiv = true;
break;
case HTML_O_CONTENT:
aContent = pOption->GetString();
break;
}
}
if ( bHTTPEquiv || HTML_META_DESCRIPTION != nAction )
{
// if it is not a Description, remove CRs and LFs from CONTENT
aContent.EraseAllChars( _CR );
aContent.EraseAllChars( _LF );
}
else
{
// convert line endings for Description
aContent.ConvertLineEnd();
}
if ( bHTTPEquiv && i_pHTTPHeader )
{
// #57232#: 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() &&
aContent.GetTokenCount() == 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<beans::XPropertyContainer> 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<document::XDocumentProperties> & 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 ); // #89973#
SetSrcEncoding( eEnc );
}
return bRet;
}
rtl_TextEncoding HTMLParser::GetEncodingByMIME( const String& rMime )
{
ByteString sType;
ByteString sSubType;
INetContentTypeParameterList aParameters;
ByteString sMime( rMime, RTL_TEXTENCODING_ASCII_US );
if (INetContentTypes::parse(sMime, sType, sSubType, &aParameters))
{
const INetContentTypeParameter * pCharset
= aParameters.find("charset");
if (pCharset != 0)
{
ByteString sValue( pCharset->m_sValue, RTL_TEXTENCODING_ASCII_US );
return GetExtendedCompatibilityTextEncoding(
rtl_getTextEncodingFromMimeCharset( sValue.GetBuffer() ) );
}
}
return RTL_TEXTENCODING_DONTKNOW;
}
rtl_TextEncoding HTMLParser::GetEncodingByHttpHeader( SvKeyValueIterator *pHTTPHeader )
{
rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW;
if( pHTTPHeader )
{
SvKeyValue aKV;
for( sal_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;
}
sal_Bool HTMLParser::SetEncodingByHTTPHeader(
SvKeyValueIterator *pHTTPHeader )
{
sal_Bool bRet = sal_False;
rtl_TextEncoding eEnc = HTMLParser::GetEncodingByHttpHeader( pHTTPHeader );
if(RTL_TEXTENCODING_DONTKNOW != eEnc)
{
SetSrcEncoding( eEnc );
bRet = sal_True;
}
return bRet;
}