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