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