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