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