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