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 <rtl/tencinfo.h>
22 : #include <tools/stream.hxx>
23 : #include <tools/debug.hxx>
24 : #include <svtools/rtftoken.h>
25 : #include <svtools/rtfkeywd.hxx>
26 : #include <svtools/parrtf.hxx>
27 : #include <comphelper/string.hxx>
28 :
29 : const int MAX_STRING_LEN = 1024;
30 : const int MAX_TOKEN_LEN = 128;
31 :
32 : #define RTF_ISDIGIT( c ) comphelper::string::isdigitAscii(c)
33 : #define RTF_ISALPHA( c ) comphelper::string::isalphaAscii(c)
34 :
35 0 : SvRTFParser::SvRTFParser( SvStream& rIn, sal_uInt8 nStackSize )
36 : : SvParser( rIn, nStackSize )
37 : , nOpenBrakets(0)
38 : , eCodeSet(RTL_TEXTENCODING_MS_1252)
39 : , eUNICodeSet(RTL_TEXTENCODING_MS_1252) // default ist ANSI-CodeSet
40 0 : , nUCharOverread(1)
41 : {
42 : // default ist ANSI-CodeSet
43 0 : SetSrcEncoding( RTL_TEXTENCODING_MS_1252 );
44 0 : bRTF_InTextRead = false;
45 0 : }
46 :
47 0 : SvRTFParser::~SvRTFParser()
48 : {
49 0 : }
50 :
51 :
52 :
53 :
54 0 : int SvRTFParser::_GetNextToken()
55 : {
56 0 : int nRet = 0;
57 0 : do {
58 0 : bool bNextCh = true;
59 0 : switch( nNextCh )
60 : {
61 : case '\\':
62 : {
63 : // control charaters
64 0 : switch( nNextCh = GetNextChar() )
65 : {
66 : case '{':
67 : case '}':
68 : case '\\':
69 : case '+': // I found it in a RTF-file
70 : case '~': // nonbreaking space
71 : case '-': // optional hyphen
72 : case '_': // nonbreaking hyphen
73 : case '\'': // HexValue
74 0 : nNextCh = '\\';
75 0 : rInput.SeekRel( -1 );
76 0 : ScanText();
77 0 : nRet = RTF_TEXTTOKEN;
78 0 : bNextCh = 0 == nNextCh;
79 0 : break;
80 :
81 : case '*': // ignoreflag
82 0 : nRet = RTF_IGNOREFLAG;
83 0 : break;
84 : case ':': // subentry in an index entry
85 0 : nRet = RTF_SUBENTRYINDEX;
86 0 : break;
87 : case '|': // formula-character
88 0 : nRet = RTF_FORMULA;
89 0 : break;
90 :
91 : case 0x0a:
92 : case 0x0d:
93 0 : nRet = RTF_PAR;
94 0 : break;
95 :
96 : default:
97 0 : if( RTF_ISALPHA( nNextCh ) )
98 : {
99 0 : aToken = "\\";
100 : {
101 0 : OUStringBuffer aStrBuffer;
102 0 : aStrBuffer.setLength( MAX_TOKEN_LEN );
103 0 : sal_Int32 nStrLen = 0;
104 0 : do {
105 0 : aStrBuffer[nStrLen++] = nNextCh;
106 0 : if( MAX_TOKEN_LEN == nStrLen )
107 : {
108 0 : aToken += aStrBuffer.toString();
109 0 : nStrLen = 0;
110 : }
111 0 : nNextCh = GetNextChar();
112 0 : } while( RTF_ISALPHA( nNextCh ) );
113 0 : if( nStrLen )
114 : {
115 0 : aToken += aStrBuffer.makeStringAndClear();
116 0 : }
117 : }
118 :
119 : // minus before numeric parameters
120 0 : bool bNegValue = false;
121 0 : if( '-' == nNextCh )
122 : {
123 0 : bNegValue = true;
124 0 : nNextCh = GetNextChar();
125 : }
126 :
127 : // possible numeric parameter
128 0 : if( RTF_ISDIGIT( nNextCh ) )
129 : {
130 0 : nTokenValue = 0;
131 0 : do {
132 0 : nTokenValue *= 10;
133 0 : nTokenValue += nNextCh - '0';
134 0 : nNextCh = GetNextChar();
135 0 : } while( RTF_ISDIGIT( nNextCh ) );
136 0 : if( bNegValue )
137 0 : nTokenValue = -nTokenValue;
138 0 : bTokenHasValue=true;
139 : }
140 0 : else if( bNegValue ) // restore minus
141 : {
142 0 : nNextCh = '-';
143 0 : rInput.SeekRel( -1 );
144 : }
145 0 : if( ' ' == nNextCh ) // blank is part of token!
146 0 : nNextCh = GetNextChar();
147 :
148 : // search for the token in the table:
149 0 : if( 0 == (nRet = GetRTFToken( aToken )) )
150 : // Unknown Control
151 0 : nRet = RTF_UNKNOWNCONTROL;
152 :
153 : // bug 76812 - unicode token handled as normal text
154 0 : bNextCh = false;
155 0 : switch( nRet )
156 : {
157 : case RTF_UC:
158 0 : if( 0 <= nTokenValue )
159 : {
160 0 : nUCharOverread = (sal_uInt8)nTokenValue;
161 : //cmc: other ifdef breaks #i3584
162 0 : aParserStates.top().
163 0 : nUCharOverread = nUCharOverread;
164 : }
165 0 : aToken.clear(); // #i47831# erase token to prevent the token from being treated as text
166 : // read next token
167 0 : nRet = 0;
168 0 : break;
169 :
170 : case RTF_UPR:
171 0 : if (!_inSkipGroup) {
172 : // UPR - overread the group with the ansi
173 : // information
174 0 : while( '{' != _GetNextToken() )
175 : ;
176 0 : SkipGroup();
177 0 : _GetNextToken(); // overread the last bracket
178 0 : nRet = 0;
179 : }
180 0 : break;
181 :
182 : case RTF_U:
183 0 : if( !bRTF_InTextRead )
184 : {
185 0 : nRet = RTF_TEXTTOKEN;
186 0 : aToken = OUString( (sal_Unicode)nTokenValue );
187 :
188 : // overread the next n "RTF" characters. This
189 : // can be also \{, \}, \'88
190 0 : for( sal_uInt8 m = 0; m < nUCharOverread; ++m )
191 : {
192 0 : sal_Unicode cAnsi = nNextCh;
193 0 : while( 0xD == cAnsi )
194 0 : cAnsi = GetNextChar();
195 0 : while( 0xA == cAnsi )
196 0 : cAnsi = GetNextChar();
197 :
198 0 : if( '\\' == cAnsi &&
199 0 : '\'' == ( cAnsi = GetNextChar() ))
200 : // read on HexValue
201 0 : cAnsi = GetHexValue();
202 0 : nNextCh = GetNextChar();
203 : }
204 0 : ScanText();
205 0 : bNextCh = 0 == nNextCh;
206 : }
207 0 : break;
208 : }
209 : }
210 0 : else if( SVPAR_PENDING != eState )
211 : {
212 : // Bug 34631 - "\ " read on - Blank as character
213 : // eState = SVPAR_ERROR;
214 0 : bNextCh = false;
215 : }
216 0 : break;
217 : }
218 : }
219 0 : break;
220 :
221 : case sal_Unicode(EOF):
222 0 : eState = SVPAR_ACCEPTED;
223 0 : nRet = nNextCh;
224 0 : break;
225 :
226 : case '{':
227 : {
228 0 : if( 0 <= nOpenBrakets )
229 : {
230 0 : RtfParserState_Impl aState( nUCharOverread, GetSrcEncoding() );
231 0 : aParserStates.push( aState );
232 : }
233 0 : ++nOpenBrakets;
234 : DBG_ASSERT(
235 : static_cast<size_t>(nOpenBrakets) == aParserStates.size(),
236 : "ParserStateStack unequal to bracket count" );
237 0 : nRet = nNextCh;
238 : }
239 0 : break;
240 :
241 : case '}':
242 0 : --nOpenBrakets;
243 0 : if( 0 <= nOpenBrakets )
244 : {
245 0 : aParserStates.pop();
246 0 : if( !aParserStates.empty() )
247 : {
248 : const RtfParserState_Impl& rRPS =
249 0 : aParserStates.top();
250 0 : nUCharOverread = rRPS.nUCharOverread;
251 0 : SetSrcEncoding( rRPS.eCodeSet );
252 : }
253 : else
254 : {
255 0 : nUCharOverread = 1;
256 0 : SetSrcEncoding( GetCodeSet() );
257 : }
258 : }
259 : DBG_ASSERT(
260 : static_cast<size_t>(nOpenBrakets) == aParserStates.size(),
261 : "ParserStateStack unequal to bracket count" );
262 0 : nRet = nNextCh;
263 0 : break;
264 :
265 : case 0x0d:
266 : case 0x0a:
267 0 : break;
268 :
269 : default:
270 : // now normal text follows
271 0 : ScanText();
272 0 : nRet = RTF_TEXTTOKEN;
273 0 : bNextCh = 0 == nNextCh;
274 0 : break;
275 : }
276 :
277 0 : if( bNextCh )
278 0 : nNextCh = GetNextChar();
279 :
280 0 : } while( !nRet && SVPAR_WORKING == eState );
281 0 : return nRet;
282 : }
283 :
284 :
285 0 : sal_Unicode SvRTFParser::GetHexValue()
286 : {
287 : // collect Hex values
288 : int n;
289 0 : sal_Unicode nHexVal = 0;
290 :
291 0 : for( n = 0; n < 2; ++n )
292 : {
293 0 : nHexVal *= 16;
294 0 : nNextCh = GetNextChar();
295 0 : if( nNextCh >= '0' && nNextCh <= '9' )
296 0 : nHexVal += (nNextCh - 48);
297 0 : else if( nNextCh >= 'a' && nNextCh <= 'f' )
298 0 : nHexVal += (nNextCh - 87);
299 0 : else if( nNextCh >= 'A' && nNextCh <= 'F' )
300 0 : nHexVal += (nNextCh - 55);
301 : }
302 0 : return nHexVal;
303 : }
304 :
305 0 : void SvRTFParser::ScanText( const sal_Unicode cBreak )
306 : {
307 0 : OUStringBuffer aStrBuffer;
308 0 : bool bContinue = true;
309 0 : while( bContinue && IsParserWorking() && aStrBuffer.getLength() < MAX_STRING_LEN)
310 : {
311 0 : bool bNextCh = true;
312 0 : switch( nNextCh )
313 : {
314 : case '\\':
315 : {
316 0 : switch (nNextCh = GetNextChar())
317 : {
318 : case '\'':
319 : {
320 :
321 0 : OStringBuffer aByteString;
322 : while (true)
323 : {
324 0 : char c = (char)GetHexValue();
325 : /*
326 : * Note: \'00 is a valid internal character in a
327 : * string in RTF. OStringBuffer supports
328 : * appending nulls fine
329 : */
330 0 : aByteString.append(c);
331 :
332 0 : bool bBreak = false;
333 0 : sal_Char nSlash = '\\';
334 0 : while (!bBreak)
335 : {
336 0 : wchar_t __next=GetNextChar();
337 0 : if (__next>0xFF) // fix for #i43933# and #i35653#
338 : {
339 0 : if (!aByteString.isEmpty())
340 0 : aStrBuffer.append( OStringToOUString(aByteString.makeStringAndClear(), GetSrcEncoding()) );
341 0 : aStrBuffer.append((sal_Unicode)__next);
342 :
343 0 : continue;
344 : }
345 0 : nSlash = (sal_Char)__next;
346 0 : while (nSlash == 0xD || nSlash == 0xA)
347 0 : nSlash = (sal_Char)GetNextChar();
348 :
349 0 : switch (nSlash)
350 : {
351 : case '{':
352 : case '}':
353 : case '\\':
354 0 : bBreak = true;
355 0 : break;
356 : default:
357 0 : aByteString.append(nSlash);
358 0 : break;
359 : }
360 : }
361 :
362 0 : nNextCh = GetNextChar();
363 :
364 0 : if (nSlash != '\\' || nNextCh != '\'')
365 : {
366 0 : rInput.SeekRel(-1);
367 0 : nNextCh = nSlash;
368 0 : break;
369 : }
370 : }
371 :
372 0 : bNextCh = false;
373 :
374 0 : if (!aByteString.isEmpty())
375 0 : aStrBuffer.append( OStringToOUString(aByteString.makeStringAndClear(), GetSrcEncoding()) );
376 : }
377 0 : break;
378 : case '\\':
379 : case '}':
380 : case '{':
381 : case '+': // I found in a RTF file
382 0 : aStrBuffer.append(nNextCh);
383 0 : break;
384 : case '~': // nonbreaking space
385 0 : aStrBuffer.append(static_cast< sal_Unicode >(0xA0));
386 0 : break;
387 : case '-': // optional hyphen
388 0 : aStrBuffer.append(static_cast< sal_Unicode >(0xAD));
389 0 : break;
390 : case '_': // nonbreaking hyphen
391 0 : aStrBuffer.append(static_cast< sal_Unicode >(0x2011));
392 0 : break;
393 :
394 : case 'u':
395 : // read UNI-Code characters
396 : {
397 0 : nNextCh = GetNextChar();
398 0 : rInput.SeekRel( -2 );
399 :
400 0 : if( '-' == nNextCh || RTF_ISDIGIT( nNextCh ) )
401 : {
402 0 : bRTF_InTextRead = true;
403 :
404 0 : OUString sSave( aToken );
405 0 : nNextCh = '\\';
406 : #ifdef DBG_UTIL
407 : int nToken =
408 : #endif
409 0 : _GetNextToken();
410 : DBG_ASSERT( RTF_U == nToken, "doch kein UNI-Code Zeichen" );
411 : // dont convert symbol chars
412 0 : aStrBuffer.append(static_cast< sal_Unicode >(nTokenValue));
413 :
414 : // overread the next n "RTF" characters. This
415 : // can be also \{, \}, \'88
416 0 : for( sal_uInt8 m = 0; m < nUCharOverread; ++m )
417 : {
418 0 : sal_Unicode cAnsi = nNextCh;
419 0 : while( 0xD == cAnsi )
420 0 : cAnsi = GetNextChar();
421 0 : while( 0xA == cAnsi )
422 0 : cAnsi = GetNextChar();
423 :
424 0 : if( '\\' == cAnsi &&
425 0 : '\'' == ( cAnsi = GetNextChar() ))
426 : // HexValue ueberlesen
427 0 : cAnsi = GetHexValue();
428 0 : nNextCh = GetNextChar();
429 : }
430 0 : bNextCh = false;
431 0 : aToken = sSave;
432 0 : bRTF_InTextRead = false;
433 : }
434 : else
435 : {
436 0 : nNextCh = '\\';
437 0 : bContinue = false; // abort, string together
438 : }
439 : }
440 0 : break;
441 :
442 : default:
443 0 : rInput.SeekRel( -1 );
444 0 : nNextCh = '\\';
445 0 : bContinue = false; // abort, string together
446 0 : break;
447 : }
448 : }
449 0 : break;
450 :
451 : case sal_Unicode(EOF):
452 0 : eState = SVPAR_ERROR;
453 : // continue
454 : case '{':
455 : case '}':
456 0 : bContinue = false;
457 0 : break;
458 :
459 : case 0x0a:
460 : case 0x0d:
461 0 : break;
462 :
463 : default:
464 0 : if( nNextCh == cBreak || aStrBuffer.getLength() >= MAX_STRING_LEN)
465 0 : bContinue = false;
466 : else
467 : {
468 0 : do {
469 : // all other characters end up in the text
470 0 : aStrBuffer.append(nNextCh);
471 :
472 0 : if (sal_Unicode(EOF) == (nNextCh = GetNextChar()))
473 : {
474 0 : if (!aStrBuffer.isEmpty())
475 0 : aToken += aStrBuffer.toString();
476 0 : return;
477 : }
478 : } while
479 : (
480 0 : (RTF_ISALPHA(nNextCh) || RTF_ISDIGIT(nNextCh)) &&
481 0 : (aStrBuffer.getLength() < MAX_STRING_LEN)
482 : );
483 0 : bNextCh = false;
484 : }
485 : }
486 :
487 0 : if( bContinue && bNextCh )
488 0 : nNextCh = GetNextChar();
489 : }
490 :
491 0 : if (!aStrBuffer.isEmpty())
492 0 : aToken += aStrBuffer.makeStringAndClear();
493 : }
494 :
495 :
496 : short SvRTFParser::_inSkipGroup=0;
497 :
498 0 : void SvRTFParser::SkipGroup()
499 : {
500 0 : short nBrackets=1;
501 0 : if (_inSkipGroup>0)
502 0 : return;
503 0 : _inSkipGroup++;
504 : //#i16185# fecking \bin keyword
505 0 : do
506 : {
507 0 : switch (nNextCh)
508 : {
509 : case '{':
510 0 : ++nBrackets;
511 0 : break;
512 : case '}':
513 0 : if (!--nBrackets) {
514 0 : _inSkipGroup--;
515 0 : return;
516 : }
517 0 : break;
518 : }
519 0 : int nToken = _GetNextToken();
520 0 : if (nToken == RTF_BIN)
521 : {
522 0 : rInput.SeekRel(-1);
523 0 : rInput.SeekRel(nTokenValue);
524 0 : nNextCh = GetNextChar();
525 : }
526 0 : while (nNextCh==0xa || nNextCh==0xd)
527 : {
528 0 : nNextCh = GetNextChar();
529 : }
530 0 : } while (sal_Unicode(EOF) != nNextCh && IsParserWorking());
531 :
532 0 : if( SVPAR_PENDING != eState && '}' != nNextCh )
533 0 : eState = SVPAR_ERROR;
534 0 : _inSkipGroup--;
535 : }
536 :
537 0 : void SvRTFParser::ReadUnknownData() { SkipGroup(); }
538 0 : void SvRTFParser::ReadBitmapData() { SkipGroup(); }
539 0 : void SvRTFParser::ReadOLEData() { SkipGroup(); }
540 :
541 :
542 0 : SvParserState SvRTFParser::CallParser()
543 : {
544 : sal_Char cFirstCh;
545 0 : nNextChPos = rInput.Tell();
546 0 : rInput.ReadChar( cFirstCh ); nNextCh = cFirstCh;
547 0 : eState = SVPAR_WORKING;
548 0 : nOpenBrakets = 0;
549 0 : SetSrcEncoding( eCodeSet = RTL_TEXTENCODING_MS_1252 );
550 0 : eUNICodeSet = RTL_TEXTENCODING_MS_1252; // default is ANSI-CodeSet
551 :
552 : // the first two tokens should be '{' and \\rtf !!
553 0 : if( '{' == GetNextToken() && RTF_RTF == GetNextToken() )
554 : {
555 0 : AddFirstRef();
556 0 : Continue( 0 );
557 0 : if( SVPAR_PENDING != eState )
558 0 : ReleaseRef(); // now parser is not needed anymore
559 : }
560 : else
561 0 : eState = SVPAR_ERROR;
562 :
563 0 : return eState;
564 : }
565 :
566 0 : void SvRTFParser::Continue( int nToken )
567 : {
568 : // DBG_ASSERT( SVPAR_CS_DONTKNOW == GetCharSet(),
569 : // "Characterset was changed." );
570 :
571 0 : if( !nToken )
572 0 : nToken = GetNextToken();
573 :
574 0 : while( IsParserWorking() )
575 : {
576 0 : SaveState( nToken );
577 0 : switch( nToken )
578 : {
579 : case '}':
580 0 : if( nOpenBrakets )
581 0 : goto NEXTTOKEN;
582 0 : eState = SVPAR_ACCEPTED;
583 0 : break;
584 :
585 : case '{':
586 : // a unknown group ?
587 : {
588 0 : if( RTF_IGNOREFLAG != GetNextToken() )
589 0 : nToken = SkipToken( -1 );
590 0 : else if( RTF_UNKNOWNCONTROL != GetNextToken() )
591 0 : nToken = SkipToken( -2 );
592 : else
593 : {
594 : // filter immediately
595 0 : ReadUnknownData();
596 0 : nToken = GetNextToken();
597 0 : if( '}' != nToken )
598 0 : eState = SVPAR_ERROR;
599 0 : break; // move to next token!!
600 : }
601 : }
602 0 : goto NEXTTOKEN;
603 :
604 : case RTF_UNKNOWNCONTROL:
605 0 : break; // skip unknown token
606 : case RTF_NEXTTYPE:
607 : case RTF_ANSITYPE:
608 0 : SetSrcEncoding( eCodeSet = RTL_TEXTENCODING_MS_1252 );
609 0 : break;
610 : case RTF_MACTYPE:
611 0 : SetSrcEncoding( eCodeSet = RTL_TEXTENCODING_APPLE_ROMAN );
612 0 : break;
613 : case RTF_PCTYPE:
614 0 : SetSrcEncoding( eCodeSet = RTL_TEXTENCODING_IBM_437 );
615 0 : break;
616 : case RTF_PCATYPE:
617 0 : SetSrcEncoding( eCodeSet = RTL_TEXTENCODING_IBM_850 );
618 0 : break;
619 : case RTF_ANSICPG:
620 0 : eCodeSet = rtl_getTextEncodingFromWindowsCodePage(nTokenValue);
621 0 : SetSrcEncoding(eCodeSet);
622 0 : break;
623 : default:
624 : NEXTTOKEN:
625 0 : NextToken( nToken );
626 0 : break;
627 : }
628 0 : if( IsParserWorking() )
629 0 : SaveState( 0 ); // processed till here,
630 : // continue with new token!
631 0 : nToken = GetNextToken();
632 : }
633 0 : if( SVPAR_ACCEPTED == eState && 0 < nOpenBrakets )
634 0 : eState = SVPAR_ERROR;
635 0 : }
636 :
637 0 : void SvRTFParser::SetEncoding( rtl_TextEncoding eEnc )
638 : {
639 0 : if (eEnc == RTL_TEXTENCODING_DONTKNOW)
640 0 : eEnc = GetCodeSet();
641 :
642 0 : if (!aParserStates.empty())
643 0 : aParserStates.top().eCodeSet = eEnc;
644 0 : SetSrcEncoding(eEnc);
645 0 : }
646 :
647 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|