lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / writerfilter / source / rtftok / rtfdispatchdestination.cxx
blob02e6c3da72fd545f89d8d37fa65b4d91ead6c3b2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include "rtfdocumentimpl.hxx"
12 #include <com/sun/star/document/DocumentProperties.hpp>
13 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
14 #include <com/sun/star/text/VertOrientation.hpp>
15 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
17 #include <filter/msfilter/escherex.hxx>
18 #include <rtl/character.hxx>
19 #include <tools/stream.hxx>
20 #include <sal/log.hxx>
22 #include <dmapper/DomainMapperFactory.hxx>
23 #include <ooxml/resourceids.hxx>
25 #include "rtflookahead.hxx"
26 #include "rtfreferenceproperties.hxx"
27 #include "rtfsdrimport.hxx"
28 #include "rtfskipdestination.hxx"
29 #include "rtftokenizer.hxx"
31 using namespace com::sun::star;
33 namespace writerfilter
35 namespace rtftok
37 RTFError RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword)
39 setNeedSect(true);
40 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
41 RTFSkipDestination aSkip(*this);
42 // special case \upr: ignore everything except nested \ud
43 if (Destination::UPR == m_aStates.top().eDestination && RTF_UD != nKeyword)
45 m_aStates.top().eDestination = Destination::SKIP;
46 aSkip.setParsed(false);
48 else
49 switch (nKeyword)
51 case RTF_RTF:
52 break;
53 case RTF_FONTTBL:
54 m_aStates.top().eDestination = Destination::FONTTABLE;
55 break;
56 case RTF_COLORTBL:
57 m_aStates.top().eDestination = Destination::COLORTABLE;
58 break;
59 case RTF_STYLESHEET:
60 m_aStates.top().eDestination = Destination::STYLESHEET;
61 break;
62 case RTF_FIELD:
63 m_aStates.top().eDestination = Destination::FIELD;
64 break;
65 case RTF_FLDINST:
67 // Look for the field type
68 sal_uInt64 const nPos = Strm().Tell();
69 OStringBuffer aBuf;
70 char ch = 0;
71 bool bFoundCode = false;
72 bool bInKeyword = false;
73 while (!bFoundCode && ch != '}')
75 Strm().ReadChar(ch);
76 if ('\\' == ch)
77 bInKeyword = true;
78 if (!bInKeyword && rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch)))
79 aBuf.append(ch);
80 else if (bInKeyword && rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch)))
81 bInKeyword = false;
82 if (!aBuf.isEmpty()
83 && !rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch)))
84 bFoundCode = true;
87 if (aBuf.toString() == "INCLUDEPICTURE")
89 // Extract the field argument of INCLUDEPICTURE: we handle that
90 // at a tokenizer level, as DOCX has no such field.
91 aBuf.append(ch);
92 while (true)
94 Strm().ReadChar(ch);
95 if (ch == '}')
96 break;
97 aBuf.append(ch);
99 OUString aFieldCommand
100 = OStringToOUString(aBuf.toString(), RTL_TEXTENCODING_UTF8);
101 std::tuple<OUString, std::vector<OUString>, std::vector<OUString>> aResult
102 = writerfilter::dmapper::splitFieldCommand(aFieldCommand);
103 m_aPicturePath
104 = std::get<1>(aResult).empty() ? OUString() : std::get<1>(aResult).front();
107 Strm().Seek(nPos);
109 // Form data should be handled only for form fields if any
110 if (aBuf.toString().indexOf(OString("FORM")) != -1)
111 m_bFormField = true;
113 singleChar(cFieldStart);
114 m_aStates.top().eDestination = Destination::FIELDINSTRUCTION;
116 break;
117 case RTF_FLDRSLT:
118 m_aStates.top().eDestination = Destination::FIELDRESULT;
119 break;
120 case RTF_LISTTABLE:
121 m_aStates.top().eDestination = Destination::LISTTABLE;
122 break;
123 case RTF_LISTPICTURE:
124 m_aStates.top().eDestination = Destination::LISTPICTURE;
125 m_aStates.top().bInListpicture = true;
126 break;
127 case RTF_LIST:
128 m_aStates.top().eDestination = Destination::LISTENTRY;
129 break;
130 case RTF_LISTNAME:
131 m_aStates.top().eDestination = Destination::LISTNAME;
132 break;
133 case RTF_LFOLEVEL:
134 m_aStates.top().eDestination = Destination::LFOLEVEL;
135 m_aStates.top().aTableSprms.clear();
136 break;
137 case RTF_LISTOVERRIDETABLE:
138 m_aStates.top().eDestination = Destination::LISTOVERRIDETABLE;
139 break;
140 case RTF_LISTOVERRIDE:
141 m_aStates.top().eDestination = Destination::LISTOVERRIDEENTRY;
142 break;
143 case RTF_LISTLEVEL:
144 m_aStates.top().eDestination = Destination::LISTLEVEL;
145 ++m_nListLevel;
146 break;
147 case RTF_LEVELTEXT:
148 m_aStates.top().eDestination = Destination::LEVELTEXT;
149 break;
150 case RTF_LEVELNUMBERS:
151 m_aStates.top().eDestination = Destination::LEVELNUMBERS;
152 break;
153 case RTF_SHPPICT:
154 resetFrame();
155 m_aStates.top().eDestination = Destination::SHPPICT;
156 break;
157 case RTF_PICT:
158 if (m_aStates.top().eDestination != Destination::SHAPEPROPERTYVALUE)
159 m_aStates.top().eDestination = Destination::PICT; // as character
160 else
161 m_aStates.top().eDestination
162 = Destination::SHAPEPROPERTYVALUEPICT; // anchored inside a shape
163 break;
164 case RTF_PICPROP:
165 m_aStates.top().eDestination = Destination::PICPROP;
166 break;
167 case RTF_SP:
168 m_aStates.top().eDestination = Destination::SHAPEPROPERTY;
169 break;
170 case RTF_SN:
171 m_aStates.top().eDestination = Destination::SHAPEPROPERTYNAME;
172 break;
173 case RTF_SV:
174 m_aStates.top().eDestination = Destination::SHAPEPROPERTYVALUE;
175 break;
176 case RTF_SHP:
177 m_bNeedCrOrig = m_bNeedCr;
178 m_aStates.top().eDestination = Destination::SHAPE;
179 m_aStates.top().bInShape = true;
180 break;
181 case RTF_SHPINST:
182 m_aStates.top().eDestination = Destination::SHAPEINSTRUCTION;
183 break;
184 case RTF_NESTTABLEPROPS:
185 // do not set any properties of outer table at nested table!
186 m_aStates.top().aTableCellSprms = m_aDefaultState.aTableCellSprms;
187 m_aStates.top().aTableCellAttributes = m_aDefaultState.aTableCellAttributes;
188 m_aNestedTableCellsSprms.clear();
189 m_aNestedTableCellsAttributes.clear();
190 m_nNestedCells = 0;
191 m_aStates.top().eDestination = Destination::NESTEDTABLEPROPERTIES;
192 break;
193 case RTF_HEADER:
194 case RTF_FOOTER:
195 case RTF_HEADERL:
196 case RTF_HEADERR:
197 case RTF_HEADERF:
198 case RTF_FOOTERL:
199 case RTF_FOOTERR:
200 case RTF_FOOTERF:
201 if (!m_pSuperstream)
203 Id nId = 0;
204 std::size_t nPos = m_nGroupStartPos - 1;
205 switch (nKeyword)
207 case RTF_HEADER:
208 if (!m_hasRHeader)
210 nId = NS_ooxml::LN_headerr;
211 m_hasRHeader = true;
213 break;
214 case RTF_FOOTER:
215 if (!m_hasRFooter)
217 nId = NS_ooxml::LN_footerr;
218 m_hasRFooter = true;
220 break;
221 case RTF_HEADERL:
222 nId = NS_ooxml::LN_headerl;
223 break;
224 case RTF_HEADERR:
225 nId = NS_ooxml::LN_headerr;
226 break;
227 case RTF_HEADERF:
228 if (!m_hasFHeader)
230 nId = NS_ooxml::LN_headerf;
231 m_hasFHeader = true;
233 break;
234 case RTF_FOOTERL:
235 nId = NS_ooxml::LN_footerl;
236 break;
237 case RTF_FOOTERR:
238 nId = NS_ooxml::LN_footerr;
239 break;
240 case RTF_FOOTERF:
241 if (!m_hasFFooter)
243 nId = NS_ooxml::LN_footerf;
244 m_hasFFooter = true;
246 break;
247 default:
248 break;
251 if (nId != 0)
252 m_nHeaderFooterPositions.push(std::make_pair(nId, nPos));
254 m_aStates.top().eDestination = Destination::SKIP;
256 break;
257 case RTF_FOOTNOTE:
258 checkFirstRun();
259 if (!m_pSuperstream)
261 Id nId = NS_ooxml::LN_footnote;
263 // Check if this is an endnote.
264 OStringBuffer aBuf;
265 char ch;
266 sal_uInt64 const nCurrent = Strm().Tell();
267 for (int i = 0; i < 7; ++i)
269 Strm().ReadChar(ch);
270 aBuf.append(ch);
272 Strm().Seek(nCurrent);
273 OString aKeyword = aBuf.makeStringAndClear();
274 if (aKeyword == "\\ftnalt")
275 nId = NS_ooxml::LN_endnote;
277 if (m_aStates.top().pCurrentBuffer == &m_aSuperBuffer)
278 m_aStates.top().pCurrentBuffer = nullptr;
279 bool bCustomMark = false;
280 OUString aCustomMark;
281 for (auto const& elem : m_aSuperBuffer)
283 if (std::get<0>(elem) == BUFFER_UTEXT)
285 aCustomMark = std::get<1>(elem)->getString();
286 bCustomMark = true;
289 m_aSuperBuffer.clear();
290 m_aStates.top().eDestination = Destination::FOOTNOTE;
291 Mapper().startCharacterGroup();
292 runProps();
293 if (!m_aStates.top().pCurrentBuffer)
294 resolveSubstream(m_nGroupStartPos - 1, nId, aCustomMark);
295 else
297 RTFSprms aAttributes;
298 aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1));
299 aAttributes.set(Id(1), new RTFValue(nId));
300 aAttributes.set(Id(2), new RTFValue(aCustomMark));
301 m_aStates.top().pCurrentBuffer->push_back(
302 Buf_t(BUFFER_RESOLVESUBSTREAM, new RTFValue(aAttributes), nullptr));
304 if (bCustomMark)
306 m_aStates.top().aCharacterAttributes.clear();
307 m_aStates.top().aCharacterSprms.clear();
308 auto pValue = new RTFValue(1);
309 m_aStates.top().aCharacterAttributes.set(
310 NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows, pValue);
311 text(aCustomMark);
313 Mapper().endCharacterGroup();
314 m_aStates.top().eDestination = Destination::SKIP;
316 break;
317 case RTF_BKMKSTART:
318 m_aStates.top().eDestination = Destination::BOOKMARKSTART;
319 break;
320 case RTF_BKMKEND:
321 m_aStates.top().eDestination = Destination::BOOKMARKEND;
322 break;
323 case RTF_XE:
324 m_aStates.top().eDestination = Destination::INDEXENTRY;
325 break;
326 case RTF_TC:
327 case RTF_TCN:
328 m_aStates.top().eDestination = Destination::TOCENTRY;
329 break;
330 case RTF_REVTBL:
331 m_aStates.top().eDestination = Destination::REVISIONTABLE;
332 break;
333 case RTF_ANNOTATION:
334 if (!m_pSuperstream)
336 resolveSubstream(m_nGroupStartPos - 1, NS_ooxml::LN_annotation);
337 m_aStates.top().eDestination = Destination::SKIP;
339 else
341 // If there is an author set, emit it now.
342 if (!m_aAuthor.isEmpty() || !m_aAuthorInitials.isEmpty())
344 RTFSprms aAttributes;
345 if (!m_aAuthor.isEmpty())
347 auto pValue = new RTFValue(m_aAuthor);
348 aAttributes.set(NS_ooxml::LN_CT_TrackChange_author, pValue);
350 if (!m_aAuthorInitials.isEmpty())
352 auto pValue = new RTFValue(m_aAuthorInitials);
353 aAttributes.set(NS_ooxml::LN_CT_Comment_initials, pValue);
355 writerfilter::Reference<Properties>::Pointer_t pProperties
356 = new RTFReferenceProperties(aAttributes);
357 Mapper().props(pProperties);
360 break;
361 case RTF_SHPTXT:
362 case RTF_DPTXBXTEXT:
364 bool bPictureFrame = false;
365 for (auto& rProperty : m_aStates.top().aShape.aProperties)
367 if (rProperty.first == "shapeType"
368 && rProperty.second == OUString::number(ESCHER_ShpInst_PictureFrame))
370 bPictureFrame = true;
371 break;
374 if (bPictureFrame)
375 // Skip text on picture frames.
376 m_aStates.top().eDestination = Destination::SKIP;
377 else
379 m_aStates.top().eDestination = Destination::SHAPETEXT;
380 checkFirstRun();
381 dispatchFlag(RTF_PARD);
382 m_bNeedPap = true;
383 if (nKeyword == RTF_SHPTXT)
385 if (!m_aStates.top().pCurrentBuffer)
386 m_pSdrImport->resolve(m_aStates.top().aShape, false,
387 RTFSdrImport::SHAPE);
388 else
390 auto pValue = new RTFValue(m_aStates.top().aShape);
391 m_aStates.top().pCurrentBuffer->push_back(
392 Buf_t(BUFFER_STARTSHAPE, pValue, nullptr));
397 break;
398 case RTF_FORMFIELD:
399 if (m_aStates.top().eDestination == Destination::FIELDINSTRUCTION)
400 m_aStates.top().eDestination = Destination::FORMFIELD;
401 break;
402 case RTF_FFNAME:
403 m_aStates.top().eDestination = Destination::FORMFIELDNAME;
404 break;
405 case RTF_FFL:
406 m_aStates.top().eDestination = Destination::FORMFIELDLIST;
407 break;
408 case RTF_DATAFIELD:
409 m_aStates.top().eDestination = Destination::DATAFIELD;
410 break;
411 case RTF_INFO:
412 m_aStates.top().eDestination = Destination::INFO;
413 break;
414 case RTF_CREATIM:
415 m_aStates.top().eDestination = Destination::CREATIONTIME;
416 break;
417 case RTF_REVTIM:
418 m_aStates.top().eDestination = Destination::REVISIONTIME;
419 break;
420 case RTF_PRINTIM:
421 m_aStates.top().eDestination = Destination::PRINTTIME;
422 break;
423 case RTF_AUTHOR:
424 m_aStates.top().eDestination = Destination::AUTHOR;
425 break;
426 case RTF_KEYWORDS:
427 m_aStates.top().eDestination = Destination::KEYWORDS;
428 break;
429 case RTF_OPERATOR:
430 m_aStates.top().eDestination = Destination::OPERATOR;
431 break;
432 case RTF_COMPANY:
433 m_aStates.top().eDestination = Destination::COMPANY;
434 break;
435 case RTF_COMMENT:
436 m_aStates.top().eDestination = Destination::COMMENT;
437 break;
438 case RTF_OBJECT:
440 // beginning of an OLE Object
441 m_aStates.top().eDestination = Destination::OBJECT;
443 // check if the object is in a special container (e.g. a table)
444 if (!m_aStates.top().pCurrentBuffer)
446 // the object is in a table or another container.
447 // Don't try to treat it as an OLE object (fdo#53594).
448 // Use the \result (RTF_RESULT) element of the object instead,
449 // the result element contain picture representing the OLE Object.
450 m_bObject = true;
453 break;
454 case RTF_OBJDATA:
455 // check if the object is in a special container (e.g. a table)
456 if (m_aStates.top().pCurrentBuffer)
458 // the object is in a table or another container.
459 // Use the \result (RTF_RESULT) element of the object instead,
460 // of the \objdata.
461 m_aStates.top().eDestination = Destination::SKIP;
463 else
465 m_aStates.top().eDestination = Destination::OBJDATA;
467 break;
468 case RTF_OBJCLASS:
469 m_aStates.top().eDestination = Destination::OBJCLASS;
470 break;
471 case RTF_RESULT:
472 m_aStates.top().eDestination = Destination::RESULT;
473 break;
474 case RTF_ATNDATE:
475 m_aStates.top().eDestination = Destination::ANNOTATIONDATE;
476 break;
477 case RTF_ATNAUTHOR:
478 m_aStates.top().eDestination = Destination::ANNOTATIONAUTHOR;
479 break;
480 case RTF_ATNREF:
481 m_aStates.top().eDestination = Destination::ANNOTATIONREFERENCE;
482 break;
483 case RTF_FALT:
484 m_aStates.top().eDestination = Destination::FALT;
485 break;
486 case RTF_FLYMAINCNT:
487 m_aStates.top().eDestination = Destination::FLYMAINCONTENT;
488 break;
489 case RTF_LISTTEXT:
490 // Should be ignored by any reader that understands Word 97 through Word 2007 numbering.
491 case RTF_NONESTTABLES:
492 // This destination should be ignored by readers that support nested tables.
493 m_aStates.top().eDestination = Destination::SKIP;
494 break;
495 case RTF_DO:
496 m_aStates.top().eDestination = Destination::DRAWINGOBJECT;
497 break;
498 case RTF_PN:
499 m_aStates.top().eDestination = Destination::PARAGRAPHNUMBERING;
500 break;
501 case RTF_PNTEXT:
502 // This destination should be ignored by readers that support paragraph numbering.
503 m_aStates.top().eDestination = Destination::SKIP;
504 break;
505 case RTF_PNTXTA:
506 m_aStates.top().eDestination = Destination::PARAGRAPHNUMBERING_TEXTAFTER;
507 break;
508 case RTF_PNTXTB:
509 m_aStates.top().eDestination = Destination::PARAGRAPHNUMBERING_TEXTBEFORE;
510 break;
511 case RTF_TITLE:
512 m_aStates.top().eDestination = Destination::TITLE;
513 break;
514 case RTF_SUBJECT:
515 m_aStates.top().eDestination = Destination::SUBJECT;
516 break;
517 case RTF_DOCCOMM:
518 m_aStates.top().eDestination = Destination::DOCCOMM;
519 break;
520 case RTF_ATRFSTART:
521 m_aStates.top().eDestination = Destination::ANNOTATIONREFERENCESTART;
522 break;
523 case RTF_ATRFEND:
524 m_aStates.top().eDestination = Destination::ANNOTATIONREFERENCEEND;
525 break;
526 case RTF_ATNID:
527 m_aStates.top().eDestination = Destination::ATNID;
528 break;
529 case RTF_MMATH:
530 case RTF_MOMATHPARA:
531 // Nothing to do here (just enter the destination) till RTF_MMATHPR is implemented.
532 break;
533 case RTF_MR:
534 m_aStates.top().eDestination = Destination::MR;
535 break;
536 case RTF_MCHR:
537 m_aStates.top().eDestination = Destination::MCHR;
538 break;
539 case RTF_MPOS:
540 m_aStates.top().eDestination = Destination::MPOS;
541 break;
542 case RTF_MVERTJC:
543 m_aStates.top().eDestination = Destination::MVERTJC;
544 break;
545 case RTF_MSTRIKEH:
546 m_aStates.top().eDestination = Destination::MSTRIKEH;
547 break;
548 case RTF_MDEGHIDE:
549 m_aStates.top().eDestination = Destination::MDEGHIDE;
550 break;
551 case RTF_MTYPE:
552 m_aStates.top().eDestination = Destination::MTYPE;
553 break;
554 case RTF_MGROW:
555 m_aStates.top().eDestination = Destination::MGROW;
556 break;
557 case RTF_MHIDETOP:
558 case RTF_MHIDEBOT:
559 case RTF_MHIDELEFT:
560 case RTF_MHIDERIGHT:
561 // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now.
562 m_aStates.top().eDestination = Destination::SKIP;
563 break;
564 case RTF_MSUBHIDE:
565 m_aStates.top().eDestination = Destination::MSUBHIDE;
566 break;
567 case RTF_MSUPHIDE:
568 m_aStates.top().eDestination = Destination::MSUPHIDE;
569 break;
570 case RTF_MBEGCHR:
571 m_aStates.top().eDestination = Destination::MBEGCHR;
572 break;
573 case RTF_MSEPCHR:
574 m_aStates.top().eDestination = Destination::MSEPCHR;
575 break;
576 case RTF_MENDCHR:
577 m_aStates.top().eDestination = Destination::MENDCHR;
578 break;
579 case RTF_UPR:
580 m_aStates.top().eDestination = Destination::UPR;
581 break;
582 case RTF_UD:
583 // Anything inside \ud is just normal Unicode content.
584 m_aStates.top().eDestination = Destination::NORMAL;
585 break;
586 case RTF_BACKGROUND:
587 m_aStates.top().eDestination = Destination::BACKGROUND;
588 m_aStates.top().bInBackground = true;
589 break;
590 case RTF_SHPGRP:
592 RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart());
593 if (!aLookahead.hasTable())
595 uno::Reference<drawing::XShapes> xGroupShape(
596 m_xModelFactory->createInstance("com.sun.star.drawing.GroupShape"),
597 uno::UNO_QUERY);
598 uno::Reference<drawing::XDrawPageSupplier> xDrawSupplier(m_xDstDoc,
599 uno::UNO_QUERY);
600 if (xDrawSupplier.is())
602 uno::Reference<drawing::XShape> xShape(xGroupShape, uno::UNO_QUERY);
603 // set default VertOrient before inserting
604 uno::Reference<beans::XPropertySet>(xShape, uno::UNO_QUERY)
605 ->setPropertyValue("VertOrient",
606 uno::makeAny(text::VertOrientation::NONE));
607 xDrawSupplier->getDrawPage()->add(xShape);
609 m_pSdrImport->pushParent(xGroupShape);
610 m_aStates.top().bCreatedShapeGroup = true;
612 m_aStates.top().eDestination = Destination::SHAPEGROUP;
613 m_aStates.top().bInShapeGroup = true;
615 break;
616 case RTF_FTNSEP:
617 m_aStates.top().eDestination = Destination::FOOTNOTESEPARATOR;
618 m_aStates.top().aCharacterAttributes.set(
619 NS_ooxml::LN_CT_FtnEdn_type,
620 new RTFValue(NS_ooxml::LN_Value_doc_ST_FtnEdn_separator));
621 break;
622 case RTF_USERPROPS:
623 // Container of all user-defined properties.
624 m_aStates.top().eDestination = Destination::USERPROPS;
625 if (m_xDocumentProperties.is())
626 // Create a custom document properties to be able to process them later all at once.
627 m_xDocumentProperties = document::DocumentProperties::create(m_xContext);
628 break;
629 case RTF_PROPNAME:
630 m_aStates.top().eDestination = Destination::PROPNAME;
631 break;
632 case RTF_STATICVAL:
633 m_aStates.top().eDestination = Destination::STATICVAL;
634 break;
635 default:
637 // Check if it's a math token.
638 RTFMathSymbol aSymbol;
639 aSymbol.eKeyword = nKeyword;
640 if (RTFTokenizer::lookupMathKeyword(aSymbol))
642 m_aMathBuffer.appendOpeningTag(aSymbol.nToken);
643 m_aStates.top().eDestination = aSymbol.eDestination;
644 return RTFError::OK;
647 SAL_INFO("writerfilter",
648 "TODO handle destination '" << keywordToString(nKeyword) << "'");
649 // Make sure we skip destinations (even without \*) till we don't handle them
650 m_aStates.top().eDestination = Destination::SKIP;
651 aSkip.setParsed(false);
653 break;
656 // new destination => use new destination text
657 m_aStates.top().pDestinationText = &m_aStates.top().aDestinationText;
659 return RTFError::OK;
662 } // namespace rtftok
663 } // namespace writerfilter
665 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */