Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <ctype.h>
22 : #include <stdio.h>
23 : #include <comphelper/string.hxx>
24 : #include <o3tl/ptr_container.hxx>
25 : #include <tools/stream.hxx>
26 : #include <tools/debug.hxx>
27 : #include <tools/color.hxx>
28 : #include <rtl/ustrbuf.hxx>
29 : #include <rtl/strbuf.hxx>
30 :
31 : #include <tools/tenccvt.hxx>
32 : #include <tools/datetime.hxx>
33 : #include <svl/inettype.hxx>
34 : #include <com/sun/star/beans/PropertyAttribute.hpp>
35 : #include <com/sun/star/document/XDocumentProperties.hpp>
36 :
37 : #include <svtools/parhtml.hxx>
38 : #include <svtools/htmltokn.h>
39 : #include <svtools/htmlkywd.hxx>
40 :
41 : #include <memory>
42 : #include <utility>
43 :
44 : using namespace ::com::sun::star;
45 :
46 :
47 : const sal_Int32 MAX_LEN( 1024L );
48 :
49 : const sal_Int32 MAX_ENTITY_LEN( 8L );
50 :
51 :
52 : // Tables to convert option values into strings
53 :
54 : // <INPUT TYPE=xxx>
55 : static HTMLOptionEnum const aInputTypeOptEnums[] =
56 : {
57 : { OOO_STRING_SVTOOLS_HTML_IT_text, HTML_IT_TEXT },
58 : { OOO_STRING_SVTOOLS_HTML_IT_password, HTML_IT_PASSWORD },
59 : { OOO_STRING_SVTOOLS_HTML_IT_checkbox, HTML_IT_CHECKBOX },
60 : { OOO_STRING_SVTOOLS_HTML_IT_radio, HTML_IT_RADIO },
61 : { OOO_STRING_SVTOOLS_HTML_IT_range, HTML_IT_RANGE },
62 : { OOO_STRING_SVTOOLS_HTML_IT_scribble, HTML_IT_SCRIBBLE },
63 : { OOO_STRING_SVTOOLS_HTML_IT_file, HTML_IT_FILE },
64 : { OOO_STRING_SVTOOLS_HTML_IT_hidden, HTML_IT_HIDDEN },
65 : { OOO_STRING_SVTOOLS_HTML_IT_submit, HTML_IT_SUBMIT },
66 : { OOO_STRING_SVTOOLS_HTML_IT_image, HTML_IT_IMAGE },
67 : { OOO_STRING_SVTOOLS_HTML_IT_reset, HTML_IT_RESET },
68 : { OOO_STRING_SVTOOLS_HTML_IT_button, HTML_IT_BUTTON },
69 : { 0, 0 }
70 : };
71 :
72 : // <TABLE FRAME=xxx>
73 : static HTMLOptionEnum const aTableFrameOptEnums[] =
74 : {
75 : { OOO_STRING_SVTOOLS_HTML_TF_void, HTML_TF_VOID },
76 : { OOO_STRING_SVTOOLS_HTML_TF_above, HTML_TF_ABOVE },
77 : { OOO_STRING_SVTOOLS_HTML_TF_below, HTML_TF_BELOW },
78 : { OOO_STRING_SVTOOLS_HTML_TF_hsides, HTML_TF_HSIDES },
79 : { OOO_STRING_SVTOOLS_HTML_TF_lhs, HTML_TF_LHS },
80 : { OOO_STRING_SVTOOLS_HTML_TF_rhs, HTML_TF_RHS },
81 : { OOO_STRING_SVTOOLS_HTML_TF_vsides, HTML_TF_VSIDES },
82 : { OOO_STRING_SVTOOLS_HTML_TF_box, HTML_TF_BOX },
83 : { OOO_STRING_SVTOOLS_HTML_TF_border, HTML_TF_BOX },
84 : { 0, 0 }
85 : };
86 :
87 : // <TABLE RULES=xxx>
88 : static HTMLOptionEnum const aTableRulesOptEnums[] =
89 : {
90 : { OOO_STRING_SVTOOLS_HTML_TR_none, HTML_TR_NONE },
91 : { OOO_STRING_SVTOOLS_HTML_TR_groups, HTML_TR_GROUPS },
92 : { OOO_STRING_SVTOOLS_HTML_TR_rows, HTML_TR_ROWS },
93 : { OOO_STRING_SVTOOLS_HTML_TR_cols, HTML_TR_COLS },
94 : { OOO_STRING_SVTOOLS_HTML_TR_all, HTML_TR_ALL },
95 : { 0, 0 }
96 : };
97 :
98 120 : sal_uInt16 HTMLOption::GetEnum( const HTMLOptionEnum *pOptEnums, sal_uInt16 nDflt ) const
99 : {
100 120 : sal_uInt16 nValue = nDflt;
101 :
102 302 : while( pOptEnums->pName )
103 180 : if( aValue.equalsIgnoreAsciiCaseAscii( pOptEnums->pName ) )
104 118 : break;
105 : else
106 62 : pOptEnums++;
107 :
108 120 : if( pOptEnums->pName )
109 118 : nValue = pOptEnums->nValue;
110 :
111 120 : return nValue;
112 : }
113 :
114 62 : bool HTMLOption::GetEnum( sal_uInt16 &rEnum, const HTMLOptionEnum *pOptEnums ) const
115 : {
116 492 : while( pOptEnums->pName )
117 : {
118 416 : if( aValue.equalsIgnoreAsciiCaseAscii( pOptEnums->pName ) )
119 48 : break;
120 : else
121 368 : pOptEnums++;
122 : }
123 :
124 62 : const sal_Char *pName = pOptEnums->pName;
125 62 : if( pName )
126 48 : rEnum = pOptEnums->nValue;
127 :
128 62 : return (pName != 0);
129 : }
130 :
131 2674 : HTMLOption::HTMLOption( sal_uInt16 nTok, const OUString& rToken,
132 : const OUString& rValue )
133 : : aValue(rValue)
134 : , aToken(rToken)
135 2674 : , nToken( nTok )
136 : {
137 : DBG_ASSERT( nToken>=HTML_OPTION_START && nToken<HTML_OPTION_END,
138 : "HTMLOption: unknown token" );
139 2674 : }
140 :
141 566 : sal_uInt32 HTMLOption::GetNumber() const
142 : {
143 : DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START &&
144 : nToken<HTML_OPTION_NUMBER_END) ||
145 : (nToken>=HTML_OPTION_CONTEXT_START &&
146 : nToken<HTML_OPTION_CONTEXT_END) ||
147 : nToken==HTML_O_VALUE,
148 : "GetNumber: Option not numerical" );
149 566 : OUString aTmp(comphelper::string::stripStart(aValue, ' '));
150 566 : sal_Int32 nTmp = aTmp.toInt32();
151 566 : return nTmp >= 0 ? (sal_uInt32)nTmp : 0;
152 : }
153 :
154 0 : sal_Int32 HTMLOption::GetSNumber() const
155 : {
156 : DBG_ASSERT( (nToken>=HTML_OPTION_NUMBER_START && nToken<HTML_OPTION_NUMBER_END) ||
157 : (nToken>=HTML_OPTION_CONTEXT_START && nToken<HTML_OPTION_CONTEXT_END),
158 : "GetSNumber: Option not numerical" );
159 0 : OUString aTmp(comphelper::string::stripStart(aValue, ' '));
160 0 : return aTmp.toInt32();
161 : }
162 :
163 0 : void HTMLOption::GetNumbers( std::vector<sal_uInt32> &rNumbers, bool bSpaceDelim ) const
164 : {
165 0 : rNumbers.clear();
166 :
167 0 : if( bSpaceDelim )
168 : {
169 : // This is a very simplified scanner: it only searches all
170 : // numerals in the string.
171 0 : bool bInNum = false;
172 0 : sal_uLong nNum = 0;
173 0 : for( sal_Int32 i=0; i<aValue.getLength(); i++ )
174 : {
175 0 : sal_Unicode c = aValue[ i ];
176 0 : if( c>='0' && c<='9' )
177 : {
178 0 : nNum *= 10;
179 0 : nNum += (c - '0');
180 0 : bInNum = true;
181 : }
182 0 : else if( bInNum )
183 : {
184 0 : rNumbers.push_back( nNum );
185 0 : bInNum = false;
186 0 : nNum = 0;
187 : }
188 : }
189 0 : if( bInNum )
190 : {
191 0 : rNumbers.push_back( nNum );
192 : }
193 : }
194 : else
195 : {
196 : // Check whether numbers are separated by ',' and
197 : // insert 0 if necessary
198 0 : sal_Int32 nPos = 0;
199 0 : while( nPos < aValue.getLength() )
200 : {
201 : sal_Unicode c;
202 0 : while( nPos < aValue.getLength() &&
203 0 : ((c=aValue[nPos]) == ' ' || c == '\t' ||
204 0 : c == '\n' || c== '\r' ) )
205 0 : nPos++;
206 :
207 0 : if( nPos==aValue.getLength() )
208 0 : rNumbers.push_back(0);
209 : else
210 : {
211 0 : sal_Int32 nEnd = aValue.indexOf( (sal_Unicode)',', nPos );
212 0 : if( -1 == nEnd )
213 : {
214 0 : sal_Int32 nTmp = aValue.copy(nPos).toInt32();
215 0 : rNumbers.push_back( nTmp >= 0 ? (sal_uInt32)nTmp : 0 );
216 0 : nPos = aValue.getLength();
217 : }
218 : else
219 : {
220 0 : sal_Int32 nTmp = aValue.copy(nPos,nEnd-nPos).toInt32();
221 0 : rNumbers.push_back( nTmp >= 0 ? (sal_uInt32)nTmp : 0 );
222 0 : nPos = nEnd+1;
223 : }
224 : }
225 : }
226 : }
227 0 : }
228 :
229 540 : void HTMLOption::GetColor( Color& rColor ) const
230 : {
231 : DBG_ASSERT( (nToken>=HTML_OPTION_COLOR_START && nToken<HTML_OPTION_COLOR_END) || nToken==HTML_O_SIZE,
232 : "GetColor: Option is not a color." );
233 :
234 540 : OUString aTmp(aValue.toAsciiLowerCase());
235 540 : sal_uInt32 nColor = SAL_MAX_UINT32;
236 540 : if( '#' != aTmp[0] )
237 0 : nColor = GetHTMLColor( aTmp );
238 :
239 540 : if( SAL_MAX_UINT32 == nColor )
240 : {
241 540 : nColor = 0;
242 540 : sal_Int32 nPos = 0;
243 3780 : for (sal_uInt32 i=0; i<6; ++i)
244 : {
245 : // Whatever Netscape does to get color values,
246 : // at maximum three characters < '0' are ignored.
247 3240 : sal_Unicode c = nPos<aTmp.getLength() ? aTmp[ nPos++ ] : '0';
248 3240 : if( c < '0' )
249 : {
250 540 : c = nPos<aTmp.getLength() ? aTmp[nPos++] : '0';
251 540 : if( c < '0' )
252 0 : c = nPos<aTmp.getLength() ? aTmp[nPos++] : '0';
253 : }
254 3240 : nColor *= 16;
255 3240 : if( c >= '0' && c <= '9' )
256 196 : nColor += (c - '0');
257 3044 : else if( c >= 'a' && c <= 'f' )
258 3044 : nColor += (c + 0xa - 'a');
259 : }
260 : }
261 :
262 540 : rColor.SetRed( (sal_uInt8)((nColor & 0x00ff0000) >> 16) );
263 540 : rColor.SetGreen( (sal_uInt8)((nColor & 0x0000ff00) >> 8));
264 540 : rColor.SetBlue( (sal_uInt8)(nColor & 0x000000ff) );
265 540 : }
266 :
267 8 : HTMLInputType HTMLOption::GetInputType() const
268 : {
269 : DBG_ASSERT( nToken==HTML_O_TYPE, "GetInputType: Option not TYPE" );
270 8 : return (HTMLInputType)GetEnum( aInputTypeOptEnums, HTML_IT_TEXT );
271 : }
272 :
273 0 : HTMLTableFrame HTMLOption::GetTableFrame() const
274 : {
275 : DBG_ASSERT( nToken==HTML_O_FRAME, "GetTableFrame: Option not FRAME" );
276 0 : return (HTMLTableFrame)GetEnum( aTableFrameOptEnums, HTML_TF_VOID );
277 : }
278 :
279 0 : HTMLTableRules HTMLOption::GetTableRules() const
280 : {
281 : DBG_ASSERT( nToken==HTML_O_RULES, "GetTableRules: Option not RULES" );
282 0 : return (HTMLTableRules)GetEnum( aTableRulesOptEnums, HTML_TR_NONE );
283 : }
284 :
285 24 : HTMLParser::HTMLParser( SvStream& rIn, bool bReadNewDoc ) :
286 : SvParser( rIn ),
287 : bNewDoc(bReadNewDoc),
288 : bIsInHeader(true),
289 : bIsInBody(false),
290 : bReadListing(false),
291 : bReadXMP(false),
292 : bReadPRE(false),
293 : bReadTextArea(false),
294 : bReadScript(false),
295 : bReadStyle(false),
296 : bEndTokenFound(false),
297 : bPre_IgnoreNewPara(false),
298 : bReadNextChar(false),
299 : bReadComment(false),
300 : nPre_LinePos(0),
301 24 : mnPendingOffToken(0)
302 : {
303 : //#i76649, default to UTF-8 for HTML unless we know differently
304 24 : SetSrcEncoding(RTL_TEXTENCODING_UTF8);
305 24 : }
306 :
307 24 : HTMLParser::~HTMLParser()
308 : {
309 24 : }
310 :
311 24 : SvParserState HTMLParser::CallParser()
312 : {
313 24 : eState = SVPAR_WORKING;
314 24 : nNextCh = GetNextChar();
315 24 : SaveState( 0 );
316 :
317 24 : nPre_LinePos = 0;
318 24 : bPre_IgnoreNewPara = false;
319 :
320 24 : AddFirstRef();
321 24 : Continue( 0 );
322 24 : if( SVPAR_PENDING != eState )
323 24 : ReleaseRef(); // Parser not needed anymore
324 :
325 24 : return eState;
326 : }
327 :
328 24 : void HTMLParser::Continue( int nToken )
329 : {
330 24 : if( !nToken )
331 24 : nToken = GetNextToken();
332 :
333 976 : while( IsParserWorking() )
334 : {
335 928 : SaveState( nToken );
336 928 : nToken = FilterToken( nToken );
337 :
338 928 : if( nToken )
339 906 : NextToken( nToken );
340 :
341 928 : if( IsParserWorking() )
342 928 : SaveState( 0 ); // continue with new token
343 :
344 928 : nToken = GetNextToken();
345 : }
346 24 : }
347 :
348 6140 : int HTMLParser::FilterToken( int nToken )
349 : {
350 6140 : switch( nToken )
351 : {
352 : case sal_Unicode(EOF):
353 0 : nToken = 0;
354 0 : break; // don't pass
355 :
356 : case HTML_HEAD_OFF:
357 16 : bIsInBody = true;
358 16 : bIsInHeader = false;
359 16 : break;
360 :
361 : case HTML_HEAD_ON:
362 16 : bIsInHeader = true;
363 16 : break;
364 :
365 : case HTML_BODY_ON:
366 22 : bIsInHeader = false;
367 22 : bIsInBody = true;
368 22 : break;
369 :
370 : case HTML_FRAMESET_ON:
371 0 : bIsInHeader = false;
372 0 : bIsInBody = false;
373 0 : break;
374 :
375 : case HTML_BODY_OFF:
376 22 : bIsInBody = bReadPRE = bReadListing = bReadXMP = false;
377 22 : break;
378 :
379 : case HTML_HTML_OFF:
380 22 : nToken = 0;
381 22 : bReadPRE = bReadListing = bReadXMP = false;
382 22 : break; // HTML_ON hasn't been passed either !
383 :
384 : case HTML_PREFORMTXT_ON:
385 0 : StartPRE();
386 0 : break;
387 :
388 : case HTML_PREFORMTXT_OFF:
389 0 : FinishPRE();
390 0 : break;
391 :
392 : case HTML_LISTING_ON:
393 0 : StartListing();
394 0 : break;
395 :
396 : case HTML_LISTING_OFF:
397 0 : FinishListing();
398 0 : break;
399 :
400 : case HTML_XMP_ON:
401 0 : StartXMP();
402 0 : break;
403 :
404 : case HTML_XMP_OFF:
405 0 : FinishXMP();
406 0 : break;
407 :
408 : default:
409 6042 : if( bReadPRE )
410 0 : nToken = FilterPRE( nToken );
411 6042 : else if( bReadListing )
412 0 : nToken = FilterListing( nToken );
413 6042 : else if( bReadXMP )
414 0 : nToken = FilterXMP( nToken );
415 :
416 6042 : break;
417 : }
418 :
419 6140 : return nToken;
420 : }
421 :
422 : #define HTML_ISDIGIT( c ) comphelper::string::isdigitAscii(c)
423 : #define HTML_ISALPHA( c ) comphelper::string::isalphaAscii(c)
424 : #define HTML_ISALNUM( c ) comphelper::string::isalnumAscii(c)
425 : #define HTML_ISSPACE( c ) ( ' ' == c || (c >= 0x09 && c <= 0x0d) )
426 : #define HTML_ISPRINTABLE( c ) ( c >= 32 && c != 127)
427 : #define HTML_ISHEXDIGIT( c ) comphelper::string::isxdigitAscii(c)
428 :
429 4582 : int HTMLParser::ScanText( const sal_Unicode cBreak )
430 : {
431 4582 : OUStringBuffer sTmpBuffer( MAX_LEN );
432 4582 : bool bContinue = true;
433 4582 : bool bEqSignFound = false;
434 4582 : sal_Unicode cQuote = 0U;
435 :
436 56788 : while( bContinue && IsParserWorking() )
437 : {
438 47636 : bool bNextCh = true;
439 47636 : switch( nNextCh )
440 : {
441 : case '&':
442 0 : bEqSignFound = false;
443 0 : if( bReadXMP )
444 0 : sTmpBuffer.append( '&' );
445 : else
446 : {
447 0 : sal_uLong nStreamPos = rInput.Tell();
448 0 : sal_uLong nLinePos = GetLinePos();
449 :
450 0 : sal_Unicode cChar = 0U;
451 0 : if( '#' == (nNextCh = GetNextChar()) )
452 : {
453 0 : nNextCh = GetNextChar();
454 0 : const bool bIsHex( 'x' == nNextCh );
455 0 : const bool bIsDecOrHex( bIsHex || HTML_ISDIGIT(nNextCh) );
456 0 : if ( bIsDecOrHex )
457 : {
458 0 : if ( bIsHex )
459 : {
460 0 : nNextCh = GetNextChar();
461 0 : while ( HTML_ISHEXDIGIT(nNextCh) )
462 : {
463 : cChar = cChar * 16U +
464 0 : ( nNextCh <= '9'
465 : ? sal_Unicode( nNextCh - '0' )
466 0 : : ( nNextCh <= 'F'
467 : ? sal_Unicode( nNextCh - 'A' + 10 )
468 0 : : sal_Unicode( nNextCh - 'a' + 10 ) ) );
469 0 : nNextCh = GetNextChar();
470 : }
471 : }
472 : else
473 : {
474 0 : do
475 : {
476 0 : cChar = cChar * 10U + sal_Unicode( nNextCh - '0');
477 0 : nNextCh = GetNextChar();
478 : }
479 0 : while( HTML_ISDIGIT(nNextCh) );
480 : }
481 :
482 0 : if( RTL_TEXTENCODING_DONTKNOW != eSrcEnc &&
483 0 : RTL_TEXTENCODING_UCS2 != eSrcEnc &&
484 0 : RTL_TEXTENCODING_UTF8 != eSrcEnc &&
485 : cChar < 256 )
486 : {
487 : const sal_uInt32 convertFlags =
488 : RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
489 : RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
490 0 : RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
491 :
492 0 : sal_Char cEncodedChar = static_cast<sal_Char>(cChar);
493 0 : cChar = OUString(&cEncodedChar, 1, eSrcEnc, convertFlags).toChar();
494 0 : if( 0U == cChar )
495 : {
496 : // If the character could not be
497 : // converted, because a conversion is not
498 : // available, do no conversion at all.
499 0 : cChar = cEncodedChar;
500 : }
501 : }
502 : }
503 : else
504 0 : nNextCh = 0U;
505 : }
506 0 : else if( HTML_ISALPHA( nNextCh ) )
507 : {
508 0 : OUStringBuffer sEntityBuffer( MAX_ENTITY_LEN );
509 0 : sal_Int32 nPos = 0L;
510 0 : do
511 : {
512 0 : sEntityBuffer.append( nNextCh );
513 0 : nPos++;
514 0 : nNextCh = GetNextChar();
515 : }
516 0 : while( nPos < MAX_ENTITY_LEN && HTML_ISALNUM( nNextCh ) &&
517 0 : !rInput.IsEof() );
518 :
519 0 : if( IsParserWorking() && !rInput.IsEof() )
520 : {
521 0 : OUString sEntity(sEntityBuffer.getStr(), nPos);
522 0 : cChar = GetHTMLCharName( sEntity );
523 :
524 : // not found ( == 0 ): plain text
525 : // or a character which is inserted as attribute
526 0 : if( 0U == cChar && ';' != nNextCh )
527 : {
528 : DBG_ASSERT( rInput.Tell() - nStreamPos ==
529 : (sal_uLong)(nPos+1L)*GetCharSize(),
530 : "UTF-8 is failing here" );
531 0 : for( sal_Int32 i = nPos-1; i>1; i-- )
532 : {
533 0 : nNextCh = sEntityBuffer[i];
534 0 : sEntityBuffer.setLength( i );
535 0 : sEntity = OUString(sEntityBuffer.getStr(), i);
536 0 : cChar = GetHTMLCharName( sEntity );
537 0 : if( cChar )
538 : {
539 : rInput.SeekRel( -(long)
540 0 : ((nPos-i)*GetCharSize()) );
541 0 : nlLinePos -= sal_uInt32(nPos-i);
542 0 : nPos = i;
543 0 : ClearTxtConvContext();
544 0 : break;
545 : }
546 : }
547 : }
548 :
549 0 : if( !cChar ) // unknown character?
550 : {
551 : // back in stream, insert '&'
552 : // and restart with next character
553 0 : sTmpBuffer.append( '&' );
554 :
555 : DBG_ASSERT( rInput.Tell()-nStreamPos ==
556 : (sal_uLong)(nPos+1)*GetCharSize(),
557 : "Wrong stream position" );
558 : DBG_ASSERT( nlLinePos-nLinePos ==
559 : (sal_uLong)(nPos+1),
560 : "Wrong line position" );
561 0 : rInput.Seek( nStreamPos );
562 0 : nlLinePos = nLinePos;
563 0 : ClearTxtConvContext();
564 0 : break;
565 : }
566 :
567 : assert(cChar != 0);
568 :
569 : // 1 == Non Breaking Space
570 : // 2 == SoftHyphen
571 :
572 0 : if (cChar == 1 || cChar == 2)
573 : {
574 0 : if( '>' == cBreak )
575 : {
576 : // When reading the content of a tag we have
577 : // to change it to ' ' or '-'
578 0 : if( 1U == cChar )
579 0 : cChar = ' ';
580 : else //2U
581 0 : cChar = '-';
582 : }
583 : else
584 : {
585 : // If not scanning a tag return token
586 0 : aToken += sTmpBuffer.makeStringAndClear();
587 :
588 0 : if( !aToken.isEmpty() )
589 : {
590 : // restart with character
591 0 : nNextCh = '&';
592 : DBG_ASSERT( rInput.Tell()-nStreamPos ==
593 : (sal_uLong)(nPos+1)*GetCharSize(),
594 : "Wrong stream position" );
595 : DBG_ASSERT( nlLinePos-nLinePos ==
596 : (sal_uLong)(nPos+1),
597 : "Wrong line position" );
598 0 : rInput.Seek( nStreamPos );
599 0 : nlLinePos = nLinePos;
600 0 : ClearTxtConvContext();
601 0 : return HTML_TEXTTOKEN;
602 : }
603 :
604 : // Hack: _GetNextChar shall not read the
605 : // next character
606 0 : if( ';' != nNextCh )
607 0 : aToken += " ";
608 0 : if( 1U == cChar )
609 0 : return HTML_NONBREAKSPACE;
610 : else //2U
611 0 : return HTML_SOFTHYPH;
612 : }
613 0 : }
614 : }
615 : else
616 0 : nNextCh = 0U;
617 : }
618 : // &{...};-JavaScript-Macros are not supported any longer.
619 0 : else if( IsParserWorking() )
620 : {
621 0 : sTmpBuffer.append( '&' );
622 0 : bNextCh = false;
623 0 : break;
624 : }
625 :
626 0 : bNextCh = (';' == nNextCh);
627 0 : if( cBreak=='>' && (cChar=='\\' || cChar=='\'' ||
628 0 : cChar=='\"' || cChar==' ') )
629 : {
630 : // ' and " have to be escaped withing tags to separate
631 : // them from ' and " enclosing options.
632 : // \ has to be escaped as well.
633 : // Space is protected because it's not a delimiter between
634 : // options.
635 0 : sTmpBuffer.append( '\\' );
636 0 : if( MAX_LEN == sTmpBuffer.getLength() )
637 0 : aToken += sTmpBuffer.makeStringAndClear();
638 : }
639 0 : if( IsParserWorking() )
640 : {
641 0 : if( cChar )
642 0 : sTmpBuffer.append( cChar );
643 : }
644 0 : else if( SVPAR_PENDING==eState && '>'!=cBreak )
645 : {
646 : // Restart with '&', the remainder is returned as
647 : // text token.
648 0 : if( !aToken.isEmpty() || !sTmpBuffer.isEmpty() )
649 : {
650 : // _GetNextChar() returns the previous text and
651 : // during the next execution a new character is read.
652 : // Thus we have to position in front of the '&'.
653 0 : nNextCh = 0U;
654 0 : rInput.Seek( nStreamPos-(sal_uInt32)GetCharSize() );
655 0 : nlLinePos = nLinePos-1;
656 0 : ClearTxtConvContext();
657 0 : bReadNextChar = true;
658 : }
659 0 : bNextCh = false;
660 : }
661 : }
662 0 : break;
663 : case '=':
664 2694 : if( '>'==cBreak && !cQuote )
665 2678 : bEqSignFound = true;
666 2694 : sTmpBuffer.append( nNextCh );
667 2694 : break;
668 :
669 : case '\\':
670 0 : if( '>'==cBreak )
671 : {
672 : // Innerhalb von Tags kennzeichnen
673 0 : sTmpBuffer.append( '\\' );
674 0 : if( MAX_LEN == sTmpBuffer.getLength() )
675 0 : aToken += sTmpBuffer.makeStringAndClear();
676 : }
677 0 : sTmpBuffer.append( '\\' );
678 0 : break;
679 :
680 : case '\"':
681 : case '\'':
682 5386 : if( '>'==cBreak )
683 : {
684 5384 : if( bEqSignFound )
685 2676 : cQuote = nNextCh;
686 2708 : else if( cQuote && (cQuote==nNextCh ) )
687 2676 : cQuote = 0U;
688 : }
689 5386 : sTmpBuffer.append( nNextCh );
690 5386 : bEqSignFound = false;
691 5386 : break;
692 :
693 : case sal_Unicode(EOF):
694 0 : if( rInput.IsEof() )
695 : {
696 0 : bContinue = false;
697 : }
698 : else
699 : {
700 0 : sTmpBuffer.append( nNextCh );
701 : }
702 0 : break;
703 :
704 : case '<':
705 2680 : bEqSignFound = false;
706 2680 : if( '>'==cBreak )
707 0 : sTmpBuffer.append( nNextCh );
708 : else
709 2680 : bContinue = false; // break, String zusammen
710 2680 : break;
711 :
712 : case '\f':
713 0 : if( '>' == cBreak )
714 : {
715 : // If scanning options treat it like a space, ...
716 0 : sTmpBuffer.append( ' ' );
717 : }
718 : else
719 : {
720 : // otherwise it's a separate token.
721 0 : bContinue = false;
722 : }
723 0 : break;
724 :
725 : case '\r':
726 : case '\n':
727 2654 : if( '>'==cBreak )
728 : {
729 : // cr/lf in tag is handled in _GetNextToken()
730 0 : sTmpBuffer.append( nNextCh );
731 0 : break;
732 : }
733 2654 : else if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
734 : {
735 0 : bContinue = false;
736 0 : break;
737 : }
738 : // Reduce sequence of CR/LF/BLANK/TAB to a single blank
739 : // no break!!
740 : case '\t':
741 2654 : if( '\t'==nNextCh && bReadPRE && '>'!=cBreak )
742 : {
743 : // Pass Tabs up in <PRE>
744 0 : bContinue = false;
745 0 : break;
746 : }
747 : // no break
748 : case '\x0b':
749 2654 : if( '\x0b'==nNextCh && (bReadPRE || bReadXMP ||bReadListing) &&
750 : '>'!=cBreak )
751 : {
752 0 : break;
753 : }
754 2654 : nNextCh = ' ';
755 : // no break;
756 : case ' ':
757 10072 : sTmpBuffer.append( nNextCh );
758 12794 : if( '>'!=cBreak && (!bReadListing && !bReadXMP &&
759 5444 : !bReadPRE && !bReadTextArea) )
760 : {
761 : // Reduce sequences of Blanks/Tabs/CR/LF to a single blank
762 9928 : do {
763 9952 : if( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
764 12 : rInput.IsEof() )
765 : {
766 12 : if( !aToken.isEmpty() || sTmpBuffer.getLength() > 1L )
767 : {
768 : // Have seen s.th. aside from blanks?
769 0 : aToken += sTmpBuffer.makeStringAndClear();
770 0 : return HTML_TEXTTOKEN;
771 : }
772 : else
773 : // Only read blanks: no text must be returned
774 : // and _GetNextToken has to read until EOF
775 12 : return 0;
776 : }
777 21892 : } while ( ' ' == nNextCh || '\t' == nNextCh ||
778 9198 : '\r' == nNextCh || '\n' == nNextCh ||
779 2710 : '\x0b' == nNextCh );
780 2710 : bNextCh = false;
781 : }
782 10060 : break;
783 :
784 : default:
785 26804 : bEqSignFound = false;
786 26804 : if (nNextCh == cBreak && !cQuote)
787 1890 : bContinue = false;
788 : else
789 : {
790 142144 : do {
791 : // All remaining characters make their way into the text.
792 142144 : sTmpBuffer.append( nNextCh );
793 142144 : if( MAX_LEN == sTmpBuffer.getLength() )
794 : {
795 54 : aToken += sTmpBuffer.makeStringAndClear();
796 : }
797 426432 : if( ( sal_Unicode(EOF) == (nNextCh = GetNextChar()) &&
798 426432 : rInput.IsEof() ) ||
799 142144 : !IsParserWorking() )
800 : {
801 0 : if( !sTmpBuffer.isEmpty() )
802 0 : aToken += sTmpBuffer.makeStringAndClear();
803 0 : return HTML_TEXTTOKEN;
804 : }
805 142144 : } while( HTML_ISALPHA( nNextCh ) || HTML_ISDIGIT( nNextCh ) );
806 24914 : bNextCh = false;
807 : }
808 : }
809 :
810 47624 : if( MAX_LEN == sTmpBuffer.getLength() )
811 0 : aToken += sTmpBuffer.makeStringAndClear();
812 :
813 47624 : if( bContinue && bNextCh )
814 15430 : nNextCh = GetNextChar();
815 : }
816 :
817 4570 : if( !sTmpBuffer.isEmpty() )
818 4570 : aToken += sTmpBuffer.makeStringAndClear();
819 :
820 4570 : return HTML_TEXTTOKEN;
821 : }
822 :
823 94 : int HTMLParser::_GetNextRawToken()
824 : {
825 94 : OUStringBuffer sTmpBuffer( MAX_LEN );
826 :
827 94 : if( bEndTokenFound )
828 : {
829 : // During the last execution we already found the end token,
830 : // thus we don't have to search it again.
831 12 : bReadScript = false;
832 12 : bReadStyle = false;
833 12 : aEndToken = "";
834 12 : bEndTokenFound = false;
835 :
836 12 : return 0;
837 : }
838 :
839 : // Default return value: HTML_RAWDATA
840 82 : bool bContinue = true;
841 82 : int nToken = HTML_RAWDATA;
842 82 : SaveState( 0 );
843 2168 : while( bContinue && IsParserWorking() )
844 : {
845 2004 : bool bNextCh = true;
846 2004 : switch( nNextCh )
847 : {
848 : case '<':
849 : {
850 : // Maybe we've reached the end.
851 :
852 : // Save what we have read previously...
853 12 : aToken += sTmpBuffer.makeStringAndClear();
854 :
855 : // and remember position in stream.
856 12 : sal_uLong nStreamPos = rInput.Tell();
857 12 : sal_uLong nLineNr = GetLineNr();
858 12 : sal_uLong nLinePos = GetLinePos();
859 :
860 : // Start of an end token?
861 12 : bool bOffState = false;
862 12 : if( '/' == (nNextCh = GetNextChar()) )
863 : {
864 12 : bOffState = true;
865 12 : nNextCh = GetNextChar();
866 : }
867 0 : else if( '!' == nNextCh )
868 : {
869 0 : sTmpBuffer.append( nNextCh );
870 0 : nNextCh = GetNextChar();
871 : }
872 :
873 : // Read following letters
874 228 : while( (HTML_ISALPHA(nNextCh) || '-'==nNextCh) &&
875 192 : IsParserWorking() && sTmpBuffer.getLength() < MAX_LEN )
876 : {
877 60 : sTmpBuffer.append( nNextCh );
878 60 : nNextCh = GetNextChar();
879 : }
880 :
881 12 : OUString aTok( sTmpBuffer.toString() );
882 12 : aTok = aTok.toAsciiLowerCase();
883 12 : bool bDone = false;
884 12 : if( bReadScript || !aEndToken.isEmpty() )
885 : {
886 0 : if( !bReadComment )
887 : {
888 0 : if( aTok.startsWith( OOO_STRING_SVTOOLS_HTML_comment ) )
889 : {
890 0 : bReadComment = true;
891 : }
892 : else
893 : {
894 : // A script has to end with "</SCRIPT>". But
895 : // ">" is optional for security reasons
896 0 : bDone = bOffState &&
897 : ( bReadScript
898 0 : ? aTok.equals(OOO_STRING_SVTOOLS_HTML_script)
899 0 : : aTok.equals(aEndToken) );
900 : }
901 : }
902 0 : if( bReadComment && '>'==nNextCh && aTok.endsWith( "--" ) )
903 : {
904 : // End of comment of style <!----->
905 0 : bReadComment = false;
906 : }
907 : }
908 : else
909 : {
910 : // Style sheets can be closed by </STYLE>, </HEAD> or <BODY>
911 12 : if( bOffState )
912 36 : bDone = aTok.equals(OOO_STRING_SVTOOLS_HTML_style) ||
913 12 : aTok.equals(OOO_STRING_SVTOOLS_HTML_head);
914 : else
915 : bDone =
916 0 : aTok.equals(OOO_STRING_SVTOOLS_HTML_body);
917 : }
918 :
919 12 : if( bDone )
920 : {
921 : // Done! Return the previously read string (if requested)
922 : // and continue.
923 :
924 12 : bContinue = false;
925 :
926 : // nToken==0 means, _GetNextToken continues to read
927 12 : if( aToken.isEmpty() && (bReadStyle || bReadScript) )
928 : {
929 : // Immediately close environment (or context?)
930 : // and parse the end token
931 0 : bReadScript = false;
932 0 : bReadStyle = false;
933 0 : aEndToken = "";
934 0 : nToken = 0;
935 : }
936 : else
937 : {
938 : // Keep bReadScript/bReadStyle alive
939 : // and parse end token during next execution
940 12 : bEndTokenFound = true;
941 : }
942 :
943 : // Move backwards in stream to '<'
944 12 : rInput.Seek( nStreamPos );
945 12 : SetLineNr( nLineNr );
946 12 : SetLinePos( nLinePos );
947 12 : ClearTxtConvContext();
948 12 : nNextCh = '<';
949 :
950 : // Don't append string to token.
951 12 : sTmpBuffer.setLength( 0L );
952 : }
953 : else
954 : {
955 : // remember "</" , everything else we find in the buffer
956 0 : aToken += "<";
957 0 : if( bOffState )
958 0 : aToken += "/";
959 :
960 0 : bNextCh = false;
961 12 : }
962 : }
963 12 : break;
964 : case '-':
965 58 : sTmpBuffer.append( nNextCh );
966 58 : if( bReadComment )
967 : {
968 0 : bool bTwoMinus = false;
969 0 : nNextCh = GetNextChar();
970 0 : while( '-' == nNextCh && IsParserWorking() )
971 : {
972 0 : bTwoMinus = true;
973 :
974 0 : if( MAX_LEN == sTmpBuffer.getLength() )
975 0 : aToken += sTmpBuffer.makeStringAndClear();
976 0 : sTmpBuffer.append( nNextCh );
977 0 : nNextCh = GetNextChar();
978 : }
979 :
980 0 : if( '>' == nNextCh && IsParserWorking() && bTwoMinus )
981 0 : bReadComment = false;
982 :
983 0 : bNextCh = false;
984 : }
985 58 : break;
986 :
987 : case '\r':
988 : // \r\n? closes the current text token (even if it's empty)
989 0 : nNextCh = GetNextChar();
990 0 : if( nNextCh=='\n' )
991 0 : nNextCh = GetNextChar();
992 0 : bContinue = false;
993 0 : break;
994 : case '\n':
995 : // \n closes the current text token (even if it's empty)
996 70 : nNextCh = GetNextChar();
997 70 : bContinue = false;
998 70 : break;
999 : case sal_Unicode(EOF):
1000 : // eof closes the current text token and behaves like having read
1001 : // an end token
1002 0 : if( rInput.IsEof() )
1003 : {
1004 0 : bContinue = false;
1005 0 : if( !aToken.isEmpty() || !sTmpBuffer.isEmpty() )
1006 : {
1007 0 : bEndTokenFound = true;
1008 : }
1009 : else
1010 : {
1011 0 : bReadScript = false;
1012 0 : bReadStyle = false;
1013 0 : aEndToken = "";
1014 0 : nToken = 0;
1015 : }
1016 0 : break;
1017 : }
1018 : // no break
1019 : default:
1020 : // all remaining characters are appended to the buffer
1021 1864 : sTmpBuffer.append( nNextCh );
1022 1864 : break;
1023 : }
1024 :
1025 3958 : if( (!bContinue && !sTmpBuffer.isEmpty()) ||
1026 1954 : MAX_LEN == sTmpBuffer.getLength() )
1027 50 : aToken += sTmpBuffer.makeStringAndClear();
1028 :
1029 2004 : if( bContinue && bNextCh )
1030 1922 : nNextCh = GetNextChar();
1031 : }
1032 :
1033 82 : if( IsParserWorking() )
1034 82 : SaveState( 0 );
1035 : else
1036 0 : nToken = 0;
1037 :
1038 82 : return nToken;
1039 : }
1040 :
1041 : // Scan next token
1042 6156 : int HTMLParser::_GetNextToken()
1043 : {
1044 6156 : int nRet = 0;
1045 6156 : sSaveToken = "";
1046 :
1047 6156 : if (mnPendingOffToken)
1048 : {
1049 : // HTML_<TOKEN>_OFF generated for HTML_<TOKEN>_ON
1050 0 : nRet = mnPendingOffToken;
1051 0 : mnPendingOffToken = 0;
1052 0 : aToken = "";
1053 0 : return nRet;
1054 : }
1055 :
1056 : // Delete options
1057 6156 : if (!maOptions.empty())
1058 1344 : maOptions.clear();
1059 :
1060 6156 : if( !IsParserWorking() ) // Don't continue if already an error occurred
1061 0 : return 0;
1062 :
1063 6156 : bool bReadNextCharSave = bReadNextChar;
1064 6156 : if( bReadNextChar )
1065 : {
1066 : DBG_ASSERT( !bEndTokenFound,
1067 : "Read a character despite </SCRIPT> was read?" );
1068 0 : nNextCh = GetNextChar();
1069 0 : if( !IsParserWorking() ) // Don't continue if already an error occurred
1070 0 : return 0;
1071 0 : bReadNextChar = false;
1072 : }
1073 :
1074 6156 : if( bReadScript || bReadStyle || !aEndToken.isEmpty() )
1075 : {
1076 94 : nRet = _GetNextRawToken();
1077 94 : if( nRet || !IsParserWorking() )
1078 82 : return nRet;
1079 : }
1080 :
1081 6086 : do {
1082 6086 : bool bNextCh = true;
1083 6086 : switch( nNextCh )
1084 : {
1085 : case '<':
1086 : {
1087 3370 : sal_uLong nStreamPos = rInput.Tell();
1088 3370 : sal_uLong nLineNr = GetLineNr();
1089 3370 : sal_uLong nLinePos = GetLinePos();
1090 :
1091 3370 : bool bOffState = false;
1092 3370 : if( '/' == (nNextCh = GetNextChar()) )
1093 : {
1094 1366 : bOffState = true;
1095 1366 : nNextCh = GetNextChar();
1096 : }
1097 3370 : if( HTML_ISALPHA( nNextCh ) || '!'==nNextCh )
1098 : {
1099 3370 : OUStringBuffer sTmpBuffer;
1100 6544 : do {
1101 6544 : sTmpBuffer.append( nNextCh );
1102 6544 : if( MAX_LEN == sTmpBuffer.getLength() )
1103 0 : aToken += sTmpBuffer.makeStringAndClear();
1104 6544 : nNextCh = GetNextChar();
1105 14782 : } while( '>' != nNextCh && '/' != nNextCh && !HTML_ISSPACE( nNextCh ) &&
1106 12892 : IsParserWorking() && !rInput.IsEof() );
1107 :
1108 3370 : if( !sTmpBuffer.isEmpty() )
1109 3370 : aToken += sTmpBuffer.makeStringAndClear();
1110 :
1111 : // Skip blanks
1112 8104 : while( HTML_ISSPACE( nNextCh ) && IsParserWorking() )
1113 1364 : nNextCh = GetNextChar();
1114 :
1115 3370 : if( !IsParserWorking() )
1116 : {
1117 0 : if( SVPAR_PENDING == eState )
1118 0 : bReadNextChar = bReadNextCharSave;
1119 0 : break;
1120 : }
1121 :
1122 : // Search token in table:
1123 3370 : sSaveToken = aToken;
1124 3370 : aToken = aToken.toAsciiLowerCase();
1125 3370 : if( 0 == (nRet = GetHTMLToken( aToken )) )
1126 : // Unknown control
1127 20 : nRet = HTML_UNKNOWNCONTROL_ON;
1128 :
1129 : // If it's a token which can be switched off...
1130 3370 : if( bOffState )
1131 : {
1132 1366 : if( HTML_TOKEN_ONOFF & nRet )
1133 : {
1134 : // and there is an off token, return off token instead
1135 1366 : ++nRet;
1136 : }
1137 0 : else if( HTML_LINEBREAK!=nRet )
1138 : {
1139 : // and there is no off token, return unknown token.
1140 : // (except for </BR>, that is treated like <BR>)
1141 0 : nRet = HTML_UNKNOWNCONTROL_OFF;
1142 : }
1143 : }
1144 :
1145 3370 : if( nRet == HTML_COMMENT )
1146 : {
1147 : // fix: due to being case sensitive use sSaveToken as start of comment
1148 : // and append a blank.
1149 0 : aToken = sSaveToken;
1150 0 : if( '>'!=nNextCh )
1151 0 : aToken += " ";
1152 0 : sal_uLong nCStreamPos = 0;
1153 0 : sal_uLong nCLineNr = 0;
1154 0 : sal_uLong nCLinePos = 0;
1155 0 : sal_Int32 nCStrLen = 0;
1156 :
1157 0 : bool bDone = false;
1158 : // Read until closing -->. If not found restart at first >
1159 0 : while( !bDone && !rInput.IsEof() && IsParserWorking() )
1160 : {
1161 0 : if( '>'==nNextCh )
1162 : {
1163 0 : if( !nCStreamPos )
1164 : {
1165 0 : nCStreamPos = rInput.Tell();
1166 0 : nCStrLen = aToken.getLength();
1167 0 : nCLineNr = GetLineNr();
1168 0 : nCLinePos = GetLinePos();
1169 : }
1170 0 : bDone = aToken.getLength() >= 2 &&
1171 0 : aToken.copy(aToken.getLength()-2,2).
1172 0 : equalsAscii( "--" );
1173 0 : if( !bDone )
1174 0 : aToken += OUString(nNextCh);
1175 : }
1176 : else
1177 0 : aToken += OUString(nNextCh);
1178 0 : if( !bDone )
1179 0 : nNextCh = GetNextChar();
1180 : }
1181 0 : if( !bDone && IsParserWorking() && nCStreamPos )
1182 : {
1183 0 : rInput.Seek( nCStreamPos );
1184 0 : SetLineNr( nCLineNr );
1185 0 : SetLinePos( nCLinePos );
1186 0 : ClearTxtConvContext();
1187 0 : aToken = aToken.copy(0, nCStrLen);
1188 0 : nNextCh = '>';
1189 : }
1190 : }
1191 : else
1192 : {
1193 : // TokenString not needed anymore
1194 3370 : aToken = "";
1195 : }
1196 :
1197 : // Read until closing '>'
1198 3370 : if( '>' != nNextCh && IsParserWorking() )
1199 : {
1200 1890 : ScanText( '>' );
1201 :
1202 : // fdo#34666 fdo#36080 fdo#36390: closing "/>"?:
1203 : // generate pending HTML_<TOKEN>_OFF for HTML_<TOKEN>_ON
1204 : // Do not convert this to a single HTML_<TOKEN>_OFF
1205 : // which lead to fdo#56772.
1206 1890 : if ((HTML_TOKEN_ONOFF & nRet) && aToken.endsWith("/"))
1207 : {
1208 0 : mnPendingOffToken = nRet + 1; // HTML_<TOKEN>_ON -> HTML_<TOKEN>_OFF
1209 0 : aToken = aToken.replaceAt( aToken.getLength()-1, 1, ""); // remove trailing '/'
1210 : }
1211 1890 : if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
1212 : {
1213 : // Move back in front of < and restart there.
1214 : // Return < as text.
1215 0 : rInput.Seek( nStreamPos );
1216 0 : SetLineNr( nLineNr );
1217 0 : SetLinePos( nLinePos );
1218 0 : ClearTxtConvContext();
1219 :
1220 0 : aToken = "<";
1221 0 : nRet = HTML_TEXTTOKEN;
1222 0 : nNextCh = GetNextChar();
1223 0 : bNextCh = false;
1224 0 : break;
1225 : }
1226 : }
1227 3370 : if( SVPAR_PENDING == eState )
1228 0 : bReadNextChar = bReadNextCharSave;
1229 : }
1230 : else
1231 : {
1232 0 : if( bOffState )
1233 : {
1234 : // einfach alles wegschmeissen
1235 0 : ScanText( '>' );
1236 0 : if( sal_Unicode(EOF) == nNextCh && rInput.IsEof() )
1237 : {
1238 : // Move back in front of < and restart there.
1239 : // Return < as text.
1240 0 : rInput.Seek( nStreamPos );
1241 0 : SetLineNr( nLineNr );
1242 0 : SetLinePos( nLinePos );
1243 0 : ClearTxtConvContext();
1244 :
1245 0 : aToken = "<";
1246 0 : nRet = HTML_TEXTTOKEN;
1247 0 : nNextCh = GetNextChar();
1248 0 : bNextCh = false;
1249 0 : break;
1250 : }
1251 0 : if( SVPAR_PENDING == eState )
1252 0 : bReadNextChar = bReadNextCharSave;
1253 0 : aToken = "";
1254 : }
1255 0 : else if( '%' == nNextCh )
1256 : {
1257 0 : nRet = HTML_UNKNOWNCONTROL_ON;
1258 :
1259 0 : sal_uLong nCStreamPos = rInput.Tell();
1260 0 : sal_uLong nCLineNr = GetLineNr(), nCLinePos = GetLinePos();
1261 :
1262 0 : bool bDone = false;
1263 : // Read until closing %>. If not found restart at first >.
1264 0 : while( !bDone && !rInput.IsEof() && IsParserWorking() )
1265 : {
1266 0 : bDone = '>'==nNextCh && aToken.endsWith("%");
1267 0 : if( !bDone )
1268 : {
1269 0 : aToken += OUString(nNextCh);
1270 0 : nNextCh = GetNextChar();
1271 : }
1272 : }
1273 0 : if( !bDone && IsParserWorking() )
1274 : {
1275 0 : rInput.Seek( nCStreamPos );
1276 0 : SetLineNr( nCLineNr );
1277 0 : SetLinePos( nCLinePos );
1278 0 : ClearTxtConvContext();
1279 0 : aToken = "<%";
1280 0 : nRet = HTML_TEXTTOKEN;
1281 0 : break;
1282 : }
1283 0 : if( IsParserWorking() )
1284 : {
1285 0 : sSaveToken = aToken;
1286 0 : aToken = "";
1287 : }
1288 : }
1289 : else
1290 : {
1291 0 : aToken = "<";
1292 0 : nRet = HTML_TEXTTOKEN;
1293 0 : bNextCh = false;
1294 0 : break;
1295 : }
1296 : }
1297 :
1298 3370 : if( IsParserWorking() )
1299 : {
1300 3370 : bNextCh = '>' == nNextCh;
1301 3370 : switch( nRet )
1302 : {
1303 : case HTML_TEXTAREA_ON:
1304 0 : bReadTextArea = true;
1305 0 : break;
1306 : case HTML_TEXTAREA_OFF:
1307 0 : bReadTextArea = false;
1308 0 : break;
1309 : case HTML_SCRIPT_ON:
1310 0 : if( !bReadTextArea )
1311 0 : bReadScript = true;
1312 0 : break;
1313 : case HTML_SCRIPT_OFF:
1314 0 : if( !bReadTextArea )
1315 : {
1316 0 : bReadScript = false;
1317 : // JavaScript might modify the stream,
1318 : // thus the last character has to be read again.
1319 0 : bReadNextChar = true;
1320 0 : bNextCh = false;
1321 : }
1322 0 : break;
1323 :
1324 : case HTML_STYLE_ON:
1325 12 : bReadStyle = true;
1326 12 : break;
1327 : case HTML_STYLE_OFF:
1328 12 : bReadStyle = false;
1329 12 : break;
1330 : }
1331 : }
1332 : }
1333 3370 : break;
1334 :
1335 : case sal_Unicode(EOF):
1336 24 : if( rInput.IsEof() )
1337 : {
1338 24 : eState = SVPAR_ACCEPTED;
1339 24 : nRet = nNextCh;
1340 : }
1341 : else
1342 : {
1343 : // Read normal text.
1344 0 : goto scan_text;
1345 : }
1346 24 : break;
1347 :
1348 : case '\f':
1349 : // form feeds are passed upwards separately
1350 0 : nRet = HTML_LINEFEEDCHAR; // !!! should be FORMFEEDCHAR
1351 0 : break;
1352 :
1353 : case '\n':
1354 : case '\r':
1355 2652 : if( bReadListing || bReadXMP || bReadPRE || bReadTextArea )
1356 : {
1357 0 : sal_Unicode c = GetNextChar();
1358 0 : if( ( '\n' != nNextCh || '\r' != c ) &&
1359 0 : ( '\r' != nNextCh || '\n' != c ) )
1360 : {
1361 0 : bNextCh = false;
1362 0 : nNextCh = c;
1363 : }
1364 0 : nRet = HTML_NEWPARA;
1365 0 : break;
1366 : }
1367 : // no break !
1368 : case '\t':
1369 2652 : if( bReadPRE )
1370 : {
1371 0 : nRet = HTML_TABCHAR;
1372 0 : break;
1373 : }
1374 : // no break !
1375 : case ' ':
1376 : // no break !
1377 : default:
1378 :
1379 : scan_text:
1380 : // "normal" text to come
1381 2692 : nRet = ScanText();
1382 2692 : bNextCh = 0 == aToken.getLength();
1383 :
1384 : // the text should be processed
1385 2692 : if( !bNextCh && eState == SVPAR_PENDING )
1386 : {
1387 0 : eState = SVPAR_WORKING;
1388 0 : bReadNextChar = true;
1389 : }
1390 :
1391 2692 : break;
1392 : }
1393 :
1394 6086 : if( bNextCh && SVPAR_WORKING == eState )
1395 : {
1396 3382 : nNextCh = GetNextChar();
1397 3382 : if( SVPAR_PENDING == eState && nRet && HTML_TEXTTOKEN != nRet )
1398 : {
1399 0 : bReadNextChar = true;
1400 0 : eState = SVPAR_WORKING;
1401 : }
1402 : }
1403 :
1404 12 : } while( !nRet && SVPAR_WORKING == eState );
1405 :
1406 6074 : if( SVPAR_PENDING == eState )
1407 0 : nRet = -1; // s.th. invalid
1408 :
1409 6074 : return nRet;
1410 : }
1411 :
1412 0 : void HTMLParser::UnescapeToken()
1413 : {
1414 0 : sal_Int32 nPos=0;
1415 :
1416 0 : bool bEscape = false;
1417 0 : while( nPos < aToken.getLength() )
1418 : {
1419 0 : bool bOldEscape = bEscape;
1420 0 : bEscape = false;
1421 0 : if( '\\'==aToken[nPos] && !bOldEscape )
1422 : {
1423 0 : aToken = aToken.replaceAt( nPos, 1, "" );
1424 0 : bEscape = true;
1425 : }
1426 : else
1427 : {
1428 0 : nPos++;
1429 : }
1430 : }
1431 0 : }
1432 :
1433 2016 : const HTMLOptions& HTMLParser::GetOptions( sal_uInt16 *pNoConvertToken )
1434 : {
1435 : // If the options for the current token have already been returned,
1436 : // return them once again.
1437 2016 : if (!maOptions.empty())
1438 46 : return maOptions;
1439 :
1440 1970 : sal_Int32 nPos = 0;
1441 8554 : while( nPos < aToken.getLength() )
1442 : {
1443 : // A letter? Option beginning here.
1444 4614 : if( HTML_ISALPHA( aToken[nPos] ) )
1445 : {
1446 : int nToken;
1447 2674 : OUString aValue;
1448 2674 : sal_Int32 nStt = nPos;
1449 2674 : sal_Unicode cChar = 0;
1450 :
1451 : // Actually only certain characters allowed.
1452 : // Netscape only looks for "=" and white space (c.f.
1453 : // Mozilla: PA_FetchRequestedNameValues in lipparse/pa_mdl.c)
1454 52828 : while( nPos < aToken.getLength() && '=' != (cChar=aToken[nPos]) &&
1455 30762 : HTML_ISPRINTABLE(cChar) && !HTML_ISSPACE(cChar) )
1456 14044 : nPos++;
1457 :
1458 5348 : OUString sName( aToken.copy( nStt, nPos-nStt ) );
1459 :
1460 : // PlugIns require original token name. Convert to lower case only for searching.
1461 2674 : nToken = GetHTMLOption( sName.toAsciiLowerCase() ); // Name is ready
1462 : DBG_ASSERTWARNING( nToken!=HTML_O_UNKNOWN,
1463 : "GetOption: unknown HTML option" );
1464 26 : bool bStripCRLF = (nToken < HTML_OPTION_SCRIPT_START ||
1465 7954 : nToken >= HTML_OPTION_SCRIPT_END) &&
1466 2836 : (!pNoConvertToken || nToken != *pNoConvertToken);
1467 :
1468 8022 : while( nPos < aToken.getLength() &&
1469 5348 : ( !HTML_ISPRINTABLE( (cChar=aToken[nPos]) ) ||
1470 2674 : HTML_ISSPACE(cChar) ) )
1471 0 : nPos++;
1472 :
1473 : // Option with value?
1474 2674 : if( nPos!=aToken.getLength() && '='==cChar )
1475 : {
1476 2674 : nPos++;
1477 :
1478 8022 : while( nPos < aToken.getLength() &&
1479 5348 : ( !HTML_ISPRINTABLE( (cChar=aToken[nPos]) ) ||
1480 2674 : ' '==cChar || '\t'==cChar || '\r'==cChar || '\n'==cChar ) )
1481 0 : nPos++;
1482 :
1483 2674 : if( nPos != aToken.getLength() )
1484 : {
1485 2674 : sal_Int32 nLen = 0;
1486 2674 : nStt = nPos;
1487 2674 : if( ('"'==cChar) || ('\'')==cChar )
1488 : {
1489 2672 : sal_Unicode cEnd = cChar;
1490 2672 : nPos++; nStt++;
1491 2672 : bool bDone = false;
1492 2672 : bool bEscape = false;
1493 140164 : while( nPos < aToken.getLength() && !bDone )
1494 : {
1495 134820 : bool bOldEscape = bEscape;
1496 134820 : bEscape = false;
1497 134820 : cChar = aToken[nPos];
1498 134820 : switch( cChar )
1499 : {
1500 : case '\r':
1501 : case '\n':
1502 0 : if( bStripCRLF )
1503 0 : aToken = aToken.replaceAt( nPos, 1, "" );
1504 : else
1505 0 : nPos++, nLen++;
1506 0 : break;
1507 : case '\\':
1508 0 : if( bOldEscape )
1509 : {
1510 0 : nPos++, nLen++;
1511 : }
1512 : else
1513 : {
1514 0 : aToken = aToken.replaceAt( nPos, 1, "" );
1515 0 : bEscape = true;
1516 : }
1517 0 : break;
1518 : case '"':
1519 : case '\'':
1520 2672 : bDone = !bOldEscape && cChar==cEnd;
1521 2672 : if( !bDone )
1522 0 : nPos++, nLen++;
1523 2672 : break;
1524 : default:
1525 132148 : nPos++, nLen++;
1526 132148 : break;
1527 : }
1528 : }
1529 2672 : if( nPos!=aToken.getLength() )
1530 2672 : nPos++;
1531 : }
1532 : else
1533 : {
1534 : // More liberal than the standard: allow all printable characters
1535 2 : bool bEscape = false;
1536 2 : bool bDone = false;
1537 22 : while( nPos < aToken.getLength() && !bDone )
1538 : {
1539 18 : bool bOldEscape = bEscape;
1540 18 : bEscape = false;
1541 18 : sal_Unicode c = aToken[nPos];
1542 18 : switch( c )
1543 : {
1544 : case ' ':
1545 2 : bDone = !bOldEscape;
1546 2 : if( !bDone )
1547 0 : nPos++, nLen++;
1548 2 : break;
1549 :
1550 : case '\t':
1551 : case '\r':
1552 : case '\n':
1553 0 : bDone = true;
1554 0 : break;
1555 :
1556 : case '\\':
1557 0 : if( bOldEscape )
1558 : {
1559 0 : nPos++, nLen++;
1560 : }
1561 : else
1562 : {
1563 0 : aToken = aToken.replaceAt( nPos, 1, "" );
1564 0 : bEscape = true;
1565 : }
1566 0 : break;
1567 :
1568 : default:
1569 16 : if( HTML_ISPRINTABLE( c ) )
1570 16 : nPos++, nLen++;
1571 : else
1572 0 : bDone = true;
1573 16 : break;
1574 : }
1575 : }
1576 : }
1577 :
1578 2674 : if( nLen )
1579 2670 : aValue = aToken.copy( nStt, nLen );
1580 : }
1581 : }
1582 :
1583 : // Token is known and can be saved
1584 : std::unique_ptr<HTMLOption> pOption(
1585 2674 : new HTMLOption(sal::static_int_cast<sal_uInt16>(nToken), sName, aValue));
1586 :
1587 5348 : o3tl::ptr_container::push_back(maOptions, std::move(pOption));
1588 : }
1589 : else
1590 : // Ignore white space and unexpected characters
1591 1940 : nPos++;
1592 : }
1593 :
1594 1970 : return maOptions;
1595 : }
1596 :
1597 0 : int HTMLParser::FilterPRE( int nToken )
1598 : {
1599 0 : switch( nToken )
1600 : {
1601 : // in Netscape they only have impact in not empty paragraphs
1602 : case HTML_PARABREAK_ON:
1603 0 : nToken = HTML_LINEBREAK;
1604 : //fall-through
1605 : case HTML_LINEBREAK:
1606 : case HTML_NEWPARA:
1607 0 : nPre_LinePos = 0;
1608 0 : if( bPre_IgnoreNewPara )
1609 0 : nToken = 0;
1610 0 : break;
1611 :
1612 : case HTML_TABCHAR:
1613 : {
1614 0 : sal_Int32 nSpaces = (8 - (nPre_LinePos % 8));
1615 : DBG_ASSERT( aToken.isEmpty(), "Why is the token not empty?" );
1616 0 : if (aToken.getLength() < nSpaces)
1617 : {
1618 : using comphelper::string::padToLength;
1619 0 : OUStringBuffer aBuf(aToken);
1620 0 : aToken = padToLength(aBuf, nSpaces, ' ').makeStringAndClear();
1621 : }
1622 0 : nPre_LinePos += nSpaces;
1623 0 : nToken = HTML_TEXTTOKEN;
1624 : }
1625 0 : break;
1626 : // Keep those
1627 : case HTML_TEXTTOKEN:
1628 0 : nPre_LinePos += aToken.getLength();
1629 0 : break;
1630 :
1631 : case HTML_SELECT_ON:
1632 : case HTML_SELECT_OFF:
1633 : case HTML_BODY_ON:
1634 : case HTML_FORM_ON:
1635 : case HTML_FORM_OFF:
1636 : case HTML_INPUT:
1637 : case HTML_OPTION:
1638 : case HTML_TEXTAREA_ON:
1639 : case HTML_TEXTAREA_OFF:
1640 :
1641 : case HTML_IMAGE:
1642 : case HTML_APPLET_ON:
1643 : case HTML_APPLET_OFF:
1644 : case HTML_PARAM:
1645 : case HTML_EMBED:
1646 :
1647 : case HTML_HEAD1_ON:
1648 : case HTML_HEAD1_OFF:
1649 : case HTML_HEAD2_ON:
1650 : case HTML_HEAD2_OFF:
1651 : case HTML_HEAD3_ON:
1652 : case HTML_HEAD3_OFF:
1653 : case HTML_HEAD4_ON:
1654 : case HTML_HEAD4_OFF:
1655 : case HTML_HEAD5_ON:
1656 : case HTML_HEAD5_OFF:
1657 : case HTML_HEAD6_ON:
1658 : case HTML_HEAD6_OFF:
1659 : case HTML_BLOCKQUOTE_ON:
1660 : case HTML_BLOCKQUOTE_OFF:
1661 : case HTML_ADDRESS_ON:
1662 : case HTML_ADDRESS_OFF:
1663 : case HTML_HORZRULE:
1664 :
1665 : case HTML_CENTER_ON:
1666 : case HTML_CENTER_OFF:
1667 : case HTML_DIVISION_ON:
1668 : case HTML_DIVISION_OFF:
1669 :
1670 : case HTML_SCRIPT_ON:
1671 : case HTML_SCRIPT_OFF:
1672 : case HTML_RAWDATA:
1673 :
1674 : case HTML_TABLE_ON:
1675 : case HTML_TABLE_OFF:
1676 : case HTML_CAPTION_ON:
1677 : case HTML_CAPTION_OFF:
1678 : case HTML_COLGROUP_ON:
1679 : case HTML_COLGROUP_OFF:
1680 : case HTML_COL_ON:
1681 : case HTML_COL_OFF:
1682 : case HTML_THEAD_ON:
1683 : case HTML_THEAD_OFF:
1684 : case HTML_TFOOT_ON:
1685 : case HTML_TFOOT_OFF:
1686 : case HTML_TBODY_ON:
1687 : case HTML_TBODY_OFF:
1688 : case HTML_TABLEROW_ON:
1689 : case HTML_TABLEROW_OFF:
1690 : case HTML_TABLEDATA_ON:
1691 : case HTML_TABLEDATA_OFF:
1692 : case HTML_TABLEHEADER_ON:
1693 : case HTML_TABLEHEADER_OFF:
1694 :
1695 : case HTML_ANCHOR_ON:
1696 : case HTML_ANCHOR_OFF:
1697 : case HTML_BOLD_ON:
1698 : case HTML_BOLD_OFF:
1699 : case HTML_ITALIC_ON:
1700 : case HTML_ITALIC_OFF:
1701 : case HTML_STRIKE_ON:
1702 : case HTML_STRIKE_OFF:
1703 : case HTML_STRIKETHROUGH_ON:
1704 : case HTML_STRIKETHROUGH_OFF:
1705 : case HTML_UNDERLINE_ON:
1706 : case HTML_UNDERLINE_OFF:
1707 : case HTML_BASEFONT_ON:
1708 : case HTML_BASEFONT_OFF:
1709 : case HTML_FONT_ON:
1710 : case HTML_FONT_OFF:
1711 : case HTML_BLINK_ON:
1712 : case HTML_BLINK_OFF:
1713 : case HTML_SPAN_ON:
1714 : case HTML_SPAN_OFF:
1715 : case HTML_SUBSCRIPT_ON:
1716 : case HTML_SUBSCRIPT_OFF:
1717 : case HTML_SUPERSCRIPT_ON:
1718 : case HTML_SUPERSCRIPT_OFF:
1719 : case HTML_BIGPRINT_ON:
1720 : case HTML_BIGPRINT_OFF:
1721 : case HTML_SMALLPRINT_OFF:
1722 : case HTML_SMALLPRINT_ON:
1723 :
1724 : case HTML_EMPHASIS_ON:
1725 : case HTML_EMPHASIS_OFF:
1726 : case HTML_CITIATION_ON:
1727 : case HTML_CITIATION_OFF:
1728 : case HTML_STRONG_ON:
1729 : case HTML_STRONG_OFF:
1730 : case HTML_CODE_ON:
1731 : case HTML_CODE_OFF:
1732 : case HTML_SAMPLE_ON:
1733 : case HTML_SAMPLE_OFF:
1734 : case HTML_KEYBOARD_ON:
1735 : case HTML_KEYBOARD_OFF:
1736 : case HTML_VARIABLE_ON:
1737 : case HTML_VARIABLE_OFF:
1738 : case HTML_DEFINSTANCE_ON:
1739 : case HTML_DEFINSTANCE_OFF:
1740 : case HTML_SHORTQUOTE_ON:
1741 : case HTML_SHORTQUOTE_OFF:
1742 : case HTML_LANGUAGE_ON:
1743 : case HTML_LANGUAGE_OFF:
1744 : case HTML_AUTHOR_ON:
1745 : case HTML_AUTHOR_OFF:
1746 : case HTML_PERSON_ON:
1747 : case HTML_PERSON_OFF:
1748 : case HTML_ACRONYM_ON:
1749 : case HTML_ACRONYM_OFF:
1750 : case HTML_ABBREVIATION_ON:
1751 : case HTML_ABBREVIATION_OFF:
1752 : case HTML_INSERTEDTEXT_ON:
1753 : case HTML_INSERTEDTEXT_OFF:
1754 : case HTML_DELETEDTEXT_ON:
1755 : case HTML_DELETEDTEXT_OFF:
1756 : case HTML_TELETYPE_ON:
1757 : case HTML_TELETYPE_OFF:
1758 :
1759 0 : break;
1760 :
1761 : // The remainder is treated as an unknown token.
1762 : default:
1763 0 : if( nToken )
1764 : {
1765 : nToken =
1766 0 : ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
1767 : ? HTML_UNKNOWNCONTROL_OFF
1768 0 : : HTML_UNKNOWNCONTROL_ON );
1769 : }
1770 0 : break;
1771 : }
1772 :
1773 0 : bPre_IgnoreNewPara = false;
1774 :
1775 0 : return nToken;
1776 : }
1777 :
1778 0 : int HTMLParser::FilterXMP( int nToken )
1779 : {
1780 0 : switch( nToken )
1781 : {
1782 : case HTML_NEWPARA:
1783 0 : if( bPre_IgnoreNewPara )
1784 0 : nToken = 0;
1785 : case HTML_TEXTTOKEN:
1786 : case HTML_NONBREAKSPACE:
1787 : case HTML_SOFTHYPH:
1788 0 : break; // kept
1789 :
1790 : default:
1791 0 : if( nToken )
1792 : {
1793 0 : if( (HTML_TOKEN_ONOFF & nToken) && (1 & nToken) )
1794 : {
1795 0 : sSaveToken = "</" + sSaveToken;
1796 : }
1797 : else
1798 0 : sSaveToken = "<" + sSaveToken;
1799 0 : if( !aToken.isEmpty() )
1800 : {
1801 0 : UnescapeToken();
1802 0 : sSaveToken += " ";
1803 0 : aToken = sSaveToken + aToken;
1804 : }
1805 : else
1806 0 : aToken = sSaveToken;
1807 0 : aToken += ">";
1808 0 : nToken = HTML_TEXTTOKEN;
1809 : }
1810 0 : break;
1811 : }
1812 :
1813 0 : bPre_IgnoreNewPara = false;
1814 :
1815 0 : return nToken;
1816 : }
1817 :
1818 0 : int HTMLParser::FilterListing( int nToken )
1819 : {
1820 0 : switch( nToken )
1821 : {
1822 : case HTML_NEWPARA:
1823 0 : if( bPre_IgnoreNewPara )
1824 0 : nToken = 0;
1825 : case HTML_TEXTTOKEN:
1826 : case HTML_NONBREAKSPACE:
1827 : case HTML_SOFTHYPH:
1828 0 : break; // kept
1829 :
1830 : default:
1831 0 : if( nToken )
1832 : {
1833 : nToken =
1834 0 : ( ((HTML_TOKEN_ONOFF & nToken) && (1 & nToken))
1835 : ? HTML_UNKNOWNCONTROL_OFF
1836 0 : : HTML_UNKNOWNCONTROL_ON );
1837 : }
1838 0 : break;
1839 : }
1840 :
1841 0 : bPre_IgnoreNewPara = false;
1842 :
1843 0 : return nToken;
1844 : }
1845 :
1846 8 : bool HTMLParser::InternalImgToPrivateURL( OUString& rURL )
1847 : {
1848 24 : if( rURL.getLength() < 19 || 'i' != rURL[0] ||
1849 8 : rURL.compareTo( OOO_STRING_SVTOOLS_HTML_internal_gopher, 9 ) != 0 )
1850 8 : return false;
1851 :
1852 0 : bool bFound = false;
1853 :
1854 0 : if( rURL.startsWith( OOO_STRING_SVTOOLS_HTML_internal_gopher ) )
1855 : {
1856 0 : OUString aName( rURL.copy(16) );
1857 0 : switch( aName[0] )
1858 : {
1859 : case 'b':
1860 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_binary;
1861 0 : break;
1862 : case 'i':
1863 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_image ||
1864 0 : aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_index;
1865 0 : break;
1866 : case 'm':
1867 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_menu ||
1868 0 : aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_movie;
1869 0 : break;
1870 : case 's':
1871 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_sound;
1872 0 : break;
1873 : case 't':
1874 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_telnet ||
1875 0 : aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_text;
1876 0 : break;
1877 : case 'u':
1878 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_GOPHER_unknown;
1879 0 : break;
1880 0 : }
1881 : }
1882 0 : else if( rURL.startsWith( OOO_STRING_SVTOOLS_HTML_internal_icon ) )
1883 : {
1884 0 : OUString aName( rURL.copy(14) );
1885 0 : switch( aName[0] )
1886 : {
1887 : case 'b':
1888 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_ICON_baddata;
1889 0 : break;
1890 : case 'd':
1891 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_ICON_delayed;
1892 0 : break;
1893 : case 'e':
1894 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_ICON_embed;
1895 0 : break;
1896 : case 'i':
1897 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_ICON_insecure;
1898 0 : break;
1899 : case 'n':
1900 0 : bFound = aName == OOO_STRING_SVTOOLS_HTML_INT_ICON_notfound;
1901 0 : break;
1902 0 : }
1903 : }
1904 0 : if( bFound )
1905 : {
1906 0 : OUString sTmp ( rURL );
1907 0 : rURL = OOO_STRING_SVTOOLS_HTML_private_image;
1908 0 : rURL += sTmp;
1909 : }
1910 :
1911 0 : return bFound;
1912 : }
1913 :
1914 : enum eHtmlMetas {
1915 : HTML_META_NONE = 0,
1916 : HTML_META_AUTHOR,
1917 : HTML_META_DESCRIPTION,
1918 : HTML_META_KEYWORDS,
1919 : HTML_META_REFRESH,
1920 : HTML_META_CLASSIFICATION,
1921 : HTML_META_CREATED,
1922 : HTML_META_CHANGEDBY,
1923 : HTML_META_CHANGED,
1924 : HTML_META_GENERATOR,
1925 : HTML_META_SDFOOTNOTE,
1926 : HTML_META_SDENDNOTE,
1927 : HTML_META_CONTENT_TYPE
1928 : };
1929 :
1930 : // <META NAME=xxx>
1931 : static HTMLOptionEnum const aHTMLMetaNameTable[] =
1932 : {
1933 : { OOO_STRING_SVTOOLS_HTML_META_author, HTML_META_AUTHOR },
1934 : { OOO_STRING_SVTOOLS_HTML_META_changed, HTML_META_CHANGED },
1935 : { OOO_STRING_SVTOOLS_HTML_META_changedby, HTML_META_CHANGEDBY },
1936 : { OOO_STRING_SVTOOLS_HTML_META_classification,HTML_META_CLASSIFICATION},
1937 : { OOO_STRING_SVTOOLS_HTML_META_content_type, HTML_META_CONTENT_TYPE },
1938 : { OOO_STRING_SVTOOLS_HTML_META_created, HTML_META_CREATED },
1939 : { OOO_STRING_SVTOOLS_HTML_META_description, HTML_META_DESCRIPTION },
1940 : { OOO_STRING_SVTOOLS_HTML_META_keywords, HTML_META_KEYWORDS },
1941 : { OOO_STRING_SVTOOLS_HTML_META_generator, HTML_META_GENERATOR },
1942 : { OOO_STRING_SVTOOLS_HTML_META_refresh, HTML_META_REFRESH },
1943 : { OOO_STRING_SVTOOLS_HTML_META_sdendnote, HTML_META_SDENDNOTE },
1944 : { OOO_STRING_SVTOOLS_HTML_META_sdfootnote, HTML_META_SDFOOTNOTE },
1945 : { 0, 0 }
1946 : };
1947 :
1948 :
1949 0 : void HTMLParser::AddMetaUserDefined( OUString const & )
1950 : {
1951 0 : }
1952 :
1953 66 : bool HTMLParser::ParseMetaOptionsImpl(
1954 : const uno::Reference<document::XDocumentProperties> & i_xDocProps,
1955 : SvKeyValueIterator *i_pHTTPHeader,
1956 : const HTMLOptions& aOptions,
1957 : rtl_TextEncoding& o_rEnc )
1958 : {
1959 132 : OUString aName, aContent;
1960 66 : sal_uInt16 nAction = HTML_META_NONE;
1961 66 : bool bHTTPEquiv = false, bChanged = false;
1962 :
1963 260 : for ( size_t i = aOptions.size(); i; )
1964 : {
1965 128 : const HTMLOption& aOption = aOptions[--i];
1966 128 : switch ( aOption.GetToken() )
1967 : {
1968 : case HTML_O_NAME:
1969 48 : aName = aOption.GetString();
1970 48 : if ( HTML_META_NONE==nAction )
1971 : {
1972 48 : aOption.GetEnum( nAction, aHTMLMetaNameTable );
1973 : }
1974 48 : break;
1975 : case HTML_O_HTTPEQUIV:
1976 14 : aName = aOption.GetString();
1977 14 : aOption.GetEnum( nAction, aHTMLMetaNameTable );
1978 14 : bHTTPEquiv = true;
1979 14 : break;
1980 : case HTML_O_CONTENT:
1981 62 : aContent = aOption.GetString();
1982 62 : break;
1983 : }
1984 : }
1985 :
1986 66 : if ( bHTTPEquiv || HTML_META_DESCRIPTION != nAction )
1987 : {
1988 : // if it is not a Description, remove CRs and LFs from CONTENT
1989 66 : aContent = comphelper::string::remove(aContent, '\r');
1990 66 : aContent = comphelper::string::remove(aContent, '\n');
1991 : }
1992 : else
1993 : {
1994 : // convert line endings for Description
1995 0 : aContent = convertLineEnd(aContent, GetSystemLineEnd());
1996 : }
1997 :
1998 :
1999 66 : if ( bHTTPEquiv && i_pHTTPHeader )
2000 : {
2001 : // Netscape seems to just ignore a closing ", so we do too
2002 14 : if ( aContent.endsWith("\"") )
2003 : {
2004 0 : aContent = aContent.copy( 0, aContent.getLength() - 1 );
2005 : }
2006 14 : SvKeyValue aKeyValue( aName, aContent );
2007 14 : i_pHTTPHeader->Append( aKeyValue );
2008 : }
2009 :
2010 66 : switch ( nAction )
2011 : {
2012 : case HTML_META_AUTHOR:
2013 2 : if (i_xDocProps.is()) {
2014 2 : i_xDocProps->setAuthor( aContent );
2015 2 : bChanged = true;
2016 : }
2017 2 : break;
2018 : case HTML_META_DESCRIPTION:
2019 0 : if (i_xDocProps.is()) {
2020 0 : i_xDocProps->setDescription( aContent );
2021 0 : bChanged = true;
2022 : }
2023 0 : break;
2024 : case HTML_META_KEYWORDS:
2025 0 : if (i_xDocProps.is()) {
2026 0 : i_xDocProps->setKeywords(
2027 0 : ::comphelper::string::convertCommaSeparated(aContent));
2028 0 : bChanged = true;
2029 : }
2030 0 : break;
2031 : case HTML_META_CLASSIFICATION:
2032 0 : if (i_xDocProps.is()) {
2033 0 : i_xDocProps->setSubject( aContent );
2034 0 : bChanged = true;
2035 : }
2036 0 : break;
2037 :
2038 : case HTML_META_CHANGEDBY:
2039 2 : if (i_xDocProps.is()) {
2040 2 : i_xDocProps->setModifiedBy( aContent );
2041 : }
2042 2 : break;
2043 :
2044 : case HTML_META_CREATED:
2045 : case HTML_META_CHANGED:
2046 40 : if ( i_xDocProps.is() && !aContent.isEmpty() &&
2047 20 : comphelper::string::getTokenCount(aContent, ';') == 2 )
2048 : {
2049 0 : Date aDate( (sal_uLong)aContent.getToken(0, ';').toInt32() );
2050 0 : tools::Time aTime( (sal_uLong)aContent.getToken(1, ';').toInt32() );
2051 0 : DateTime aDateTime( aDate, aTime );
2052 0 : ::util::DateTime uDT(aDateTime.GetNanoSec(),
2053 0 : aDateTime.GetSec(), aDateTime.GetMin(),
2054 0 : aDateTime.GetHour(), aDateTime.GetDay(),
2055 0 : aDateTime.GetMonth(), aDateTime.GetYear(),
2056 0 : false);
2057 0 : if ( HTML_META_CREATED==nAction )
2058 0 : i_xDocProps->setCreationDate( uDT );
2059 : else
2060 0 : i_xDocProps->setModificationDate( uDT );
2061 0 : bChanged = true;
2062 : }
2063 20 : break;
2064 :
2065 : case HTML_META_REFRESH:
2066 : DBG_ASSERT( !bHTTPEquiv || i_pHTTPHeader,
2067 : "Reload-URL aufgrund unterlassener MUSS-Aenderung verlorengegangen" );
2068 0 : break;
2069 :
2070 : case HTML_META_CONTENT_TYPE:
2071 14 : if ( !aContent.isEmpty() )
2072 : {
2073 14 : o_rEnc = GetEncodingByMIME( aContent );
2074 : }
2075 14 : break;
2076 :
2077 : case HTML_META_NONE:
2078 18 : if ( !bHTTPEquiv )
2079 : {
2080 18 : if (i_xDocProps.is())
2081 : {
2082 : uno::Reference<beans::XPropertyContainer> xUDProps
2083 18 : = i_xDocProps->getUserDefinedProperties();
2084 : try {
2085 18 : xUDProps->addProperty(aName,
2086 : beans::PropertyAttribute::REMOVABLE,
2087 18 : uno::makeAny(OUString(aContent)));
2088 18 : AddMetaUserDefined(aName);
2089 18 : bChanged = true;
2090 0 : } catch (uno::Exception &) {
2091 : // ignore
2092 18 : }
2093 : }
2094 : }
2095 18 : break;
2096 : default:
2097 10 : break;
2098 : }
2099 :
2100 132 : return bChanged;
2101 : }
2102 :
2103 66 : bool HTMLParser::ParseMetaOptions(
2104 : const uno::Reference<document::XDocumentProperties> & i_xDocProps,
2105 : SvKeyValueIterator *i_pHeader )
2106 : {
2107 66 : sal_uInt16 nContentOption = HTML_O_CONTENT;
2108 66 : rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
2109 :
2110 : bool bRet = ParseMetaOptionsImpl( i_xDocProps, i_pHeader,
2111 66 : GetOptions(&nContentOption),
2112 66 : eEnc );
2113 :
2114 : // If the encoding is set by a META tag, it may only overwrite the
2115 : // current encoding if both, the current and the new encoding, are 1-sal_uInt8
2116 : // encodings. Everything else cannot lead to reasonable results.
2117 146 : if (RTL_TEXTENCODING_DONTKNOW != eEnc &&
2118 80 : rtl_isOctetTextEncoding( eEnc ) &&
2119 14 : rtl_isOctetTextEncoding( GetSrcEncoding() ) )
2120 : {
2121 14 : eEnc = GetExtendedCompatibilityTextEncoding( eEnc );
2122 14 : SetSrcEncoding( eEnc );
2123 : }
2124 :
2125 66 : return bRet;
2126 : }
2127 :
2128 14 : rtl_TextEncoding HTMLParser::GetEncodingByMIME( const OUString& rMime )
2129 : {
2130 14 : OUString sType;
2131 28 : OUString sSubType;
2132 28 : INetContentTypeParameterList aParameters;
2133 14 : if (INetContentTypes::parse(rMime, sType, sSubType, &aParameters))
2134 : {
2135 14 : const INetContentTypeParameter * pCharset = aParameters.find("charset");
2136 14 : if (pCharset != 0)
2137 : {
2138 14 : OString sValue(OUStringToOString(pCharset->m_sValue, RTL_TEXTENCODING_ASCII_US));
2139 14 : return GetExtendedCompatibilityTextEncoding( rtl_getTextEncodingFromMimeCharset( sValue.getStr() ) );
2140 : }
2141 : }
2142 14 : return RTL_TEXTENCODING_DONTKNOW;
2143 : }
2144 :
2145 24 : rtl_TextEncoding HTMLParser::GetEncodingByHttpHeader( SvKeyValueIterator *pHTTPHeader )
2146 : {
2147 24 : rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW;
2148 24 : if( pHTTPHeader )
2149 : {
2150 24 : SvKeyValue aKV;
2151 48 : for( bool bCont = pHTTPHeader->GetFirst( aKV ); bCont;
2152 24 : bCont = pHTTPHeader->GetNext( aKV ) )
2153 : {
2154 24 : if( aKV.GetKey().equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_META_content_type ) )
2155 : {
2156 24 : if( !aKV.GetValue().isEmpty() )
2157 : {
2158 0 : eRet = HTMLParser::GetEncodingByMIME( aKV.GetValue() );
2159 : }
2160 : }
2161 24 : }
2162 : }
2163 24 : return eRet;
2164 : }
2165 :
2166 24 : bool HTMLParser::SetEncodingByHTTPHeader( SvKeyValueIterator *pHTTPHeader )
2167 : {
2168 24 : bool bRet = false;
2169 24 : rtl_TextEncoding eEnc = HTMLParser::GetEncodingByHttpHeader( pHTTPHeader );
2170 24 : if(RTL_TEXTENCODING_DONTKNOW != eEnc)
2171 : {
2172 0 : SetSrcEncoding( eEnc );
2173 0 : bRet = true;
2174 : }
2175 24 : return bRet;
2176 : }
2177 :
2178 :
2179 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|