LibreOffice Module writerfilter (master) 1
rtfdispatchdestination.cxx
Go to the documentation of this file.
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
10#include <sal/config.h>
11
12#include <string_view>
13
14#include "rtfdocumentimpl.hxx"
15
16#include <com/sun/star/document/DocumentProperties.hpp>
17#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
18#include <com/sun/star/text/VertOrientation.hpp>
19#include <com/sun/star/lang/XMultiServiceFactory.hpp>
20
22#include <rtl/character.hxx>
23#include <tools/stream.hxx>
24#include <sal/log.hxx>
25
27#include <ooxml/resourceids.hxx>
28
29#include "rtflookahead.hxx"
31#include "rtfsdrimport.hxx"
33#include "rtftokenizer.hxx"
34
35using namespace com::sun::star;
36
38{
40{
41 setNeedSect(true);
42 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
43 RTFSkipDestination aSkip(*this);
44 // special case \upr: ignore everything except nested \ud
46 {
48 aSkip.setParsed(false);
49 }
50 else
51 switch (nKeyword)
52 {
53 case RTFKeyword::RTF:
54 break;
57 break;
60 break;
63 break;
67 break;
70 break;
72 {
73 // Look for the field type
74 sal_uInt64 const nPos = Strm().Tell();
75 OStringBuffer aBuf;
76 char ch = 0;
77 bool bFoundCode = false;
78 bool bInKeyword = false;
79 while (!bFoundCode && ch != '}')
80 {
81 Strm().ReadChar(ch);
82 if ('\\' == ch)
83 bInKeyword = true;
84 if (!bInKeyword && rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch)))
85 aBuf.append(ch);
86 else if (bInKeyword && rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch)))
87 bInKeyword = false;
88 if (!aBuf.isEmpty()
89 && !rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch)))
90 bFoundCode = true;
91 }
92
93 if (std::string_view(aBuf) == "INCLUDEPICTURE")
94 {
95 // Extract the field argument of INCLUDEPICTURE: we handle that
96 // at a tokenizer level, as DOCX has no such field.
97 aBuf.append(ch);
98 while (true)
99 {
100 Strm().ReadChar(ch);
101 if (ch == '}')
102 break;
103 aBuf.append(ch);
104 }
105 OUString aFieldCommand = OStringToOUString(aBuf, RTL_TEXTENCODING_UTF8);
106 std::tuple<OUString, std::vector<OUString>, std::vector<OUString>> aResult
109 = std::get<1>(aResult).empty() ? OUString() : std::get<1>(aResult).front();
110 }
111
112 Strm().Seek(nPos);
113
114 // Form data should be handled only for form fields if any
115 if (aBuf.toString().indexOf("FORM") != -1)
116 m_bFormField = true;
117
120 }
121 break;
124 break;
127 break;
131 break;
132 case RTFKeyword::LIST:
134 break;
137 break;
141 break;
144 break;
147 break;
150 ++m_nListLevel;
151 break;
154 break;
157 break;
159 resetFrame();
161 break;
162 case RTFKeyword::PICT:
165 else
167 Destination::SHAPEPROPERTYVALUEPICT); // anchored inside a shape
168 break;
171 break;
172 case RTFKeyword::SP:
174 break;
175 case RTFKeyword::SN:
177 break;
178 case RTFKeyword::SV:
180 break;
181 case RTFKeyword::SHP:
184 m_aStates.top().setInShape(true);
185 break;
188 break;
190 // do not set any properties of outer table at nested table!
195 m_nNestedCells = 0;
197 break;
206 if (!m_pSuperstream)
207 {
208 Id nId = 0;
209 std::size_t nPos = m_nGroupStartPos - 1;
210 switch (nKeyword)
211 {
213 if (!m_hasRHeader)
214 {
215 nId = NS_ooxml::LN_headerr;
216 m_hasRHeader = true;
217 }
218 break;
220 if (!m_hasRFooter)
221 {
222 nId = NS_ooxml::LN_footerr;
223 m_hasRFooter = true;
224 }
225 break;
227 nId = NS_ooxml::LN_headerl;
228 break;
230 nId = NS_ooxml::LN_headerr;
231 break;
233 if (!m_hasFHeader)
234 {
235 nId = NS_ooxml::LN_headerf;
236 m_hasFHeader = true;
237 }
238 break;
240 nId = NS_ooxml::LN_footerl;
241 break;
243 nId = NS_ooxml::LN_footerr;
244 break;
246 if (!m_hasFFooter)
247 {
248 nId = NS_ooxml::LN_footerf;
249 m_hasFFooter = true;
250 }
251 break;
252 default:
253 break;
254 }
255
256 if (nId != 0)
257 m_nHeaderFooterPositions.push(std::make_pair(nId, nPos));
258
260 }
261 break;
264 if (!m_pSuperstream)
265 {
266 Id nId = NS_ooxml::LN_footnote;
267
268 // Check if this is an endnote.
269 OStringBuffer aBuf;
270 char ch;
271 sal_uInt64 const nCurrent = Strm().Tell();
272 for (int i = 0; i < 7; ++i)
273 {
274 Strm().ReadChar(ch);
275 aBuf.append(ch);
276 }
277 Strm().Seek(nCurrent);
278 OString aKeyword = aBuf.makeStringAndClear();
279 if (aKeyword == "\\ftnalt")
280 nId = NS_ooxml::LN_endnote;
281
283 m_aStates.top().setCurrentBuffer(nullptr);
284 bool bCustomMark = false;
285 OUString aCustomMark;
286 for (auto const& elem : m_aSuperBuffer)
287 {
288 if (std::get<0>(elem) == BUFFER_UTEXT)
289 {
290 aCustomMark = std::get<1>(elem)->getString();
291 bCustomMark = true;
292 }
293 }
294 m_aSuperBuffer.clear();
297 runProps();
299 resolveSubstream(m_nGroupStartPos - 1, nId, aCustomMark);
300 else
301 {
302 RTFSprms aAttributes;
303 aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1));
304 aAttributes.set(Id(1), new RTFValue(nId));
305 aAttributes.set(Id(2), new RTFValue(aCustomMark));
306 m_aStates.top().getCurrentBuffer()->push_back(
307 Buf_t(BUFFER_RESOLVESUBSTREAM, new RTFValue(aAttributes), nullptr));
308 }
309 if (bCustomMark)
310 {
313 auto pValue = new RTFValue(1);
315 NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows, pValue);
316 text(aCustomMark);
317 }
320 }
321 break;
324 break;
327 break;
328 case RTFKeyword::XE:
330 break;
331 case RTFKeyword::TC:
332 case RTFKeyword::TCN:
334 break;
337 break;
339 if (!m_pSuperstream)
340 {
342 {
343 resolveSubstream(m_nGroupStartPos - 1, NS_ooxml::LN_annotation);
344 }
345 else
346 {
347 RTFSprms aAttributes;
348 aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1));
349 aAttributes.set(Id(1), new RTFValue(NS_ooxml::LN_annotation));
350 aAttributes.set(Id(2), new RTFValue(OUString()));
351 m_aStates.top().getCurrentBuffer()->push_back(
352 Buf_t(BUFFER_RESOLVESUBSTREAM, new RTFValue(aAttributes), nullptr));
353 }
355 }
356 else
357 {
358 // If there is an author set, emit it now.
359 if (!m_aAuthor.isEmpty() || !m_aAuthorInitials.isEmpty())
360 {
361 RTFSprms aAttributes;
362 if (!m_aAuthor.isEmpty())
363 {
364 auto pValue = new RTFValue(m_aAuthor);
365 aAttributes.set(NS_ooxml::LN_CT_TrackChange_author, pValue);
366 }
367 if (!m_aAuthorInitials.isEmpty())
368 {
369 auto pValue = new RTFValue(m_aAuthorInitials);
370 aAttributes.set(NS_ooxml::LN_CT_Comment_initials, pValue);
371 }
373 = new RTFReferenceProperties(std::move(aAttributes));
374 Mapper().props(pProperties);
375 }
376 }
377 break;
380 {
381 bool bPictureFrame = false;
382 for (const auto& rProperty : m_aStates.top().getShape().getProperties())
383 {
384 if (rProperty.first == "shapeType"
385 && rProperty.second
386 == std::u16string_view(
387 OUString::number(ESCHER_ShpInst_PictureFrame)))
388 {
389 bPictureFrame = true;
390 break;
391 }
392 }
393 if (bPictureFrame)
394 // Skip text on picture frames.
396 else
397 {
401 m_bNeedPap = true;
402 if (nKeyword == RTFKeyword::SHPTXT)
403 {
405 m_pSdrImport->resolve(m_aStates.top().getShape(), false,
407 else
408 {
409 auto pValue = new RTFValue(m_aStates.top().getShape());
410 m_aStates.top().getCurrentBuffer()->push_back(
411 Buf_t(BUFFER_STARTSHAPE, pValue, nullptr));
412 }
413 }
414 }
415 }
416 break;
420 break;
423 break;
424 case RTFKeyword::FFL:
426 break;
429 break;
430 case RTFKeyword::INFO:
432 break;
435 break;
438 break;
441 break;
444 break;
447 break;
450 break;
453 break;
456 break;
458 {
459 // beginning of an OLE Object
461
462 // check if the object is in a special container (e.g. a table)
464 {
465 // the object is in a table or another container.
466 // Don't try to treat it as an OLE object (fdo#53594).
467 // Use the \result (RTFKeyword::RESULT) element of the object instead,
468 // the result element contain picture representing the OLE Object.
469 m_bObject = true;
470 }
471 }
472 break;
474 // check if the object is in a special container (e.g. a table)
476 {
477 // the object is in a table or another container.
478 // Use the \result (RTFKeyword::RESULT) element of the object instead,
479 // of the \objdata.
481 }
482 else
483 {
485 }
486 break;
489 break;
492 break;
495 break;
498 break;
501 break;
502 case RTFKeyword::FALT:
504 break;
507 break;
509 // Should be ignored by any reader that understands Word 97 through Word 2007 numbering.
511 // This destination should be ignored by readers that support nested tables.
513 break;
514 case RTFKeyword::DO:
516 break;
517 case RTFKeyword::PN:
519 break;
521 // This destination should be ignored by readers that support paragraph numbering.
523 break;
526 break;
529 break;
532 break;
535 break;
538 break;
541 break;
544 break;
547 break;
550 // Nothing to do here (just enter the destination) till RTFKeyword::MMATHPR is implemented.
551 break;
552 case RTFKeyword::MR:
554 break;
555 case RTFKeyword::MCHR:
557 break;
558 case RTFKeyword::MPOS:
560 break;
563 break;
566 break;
569 break;
572 break;
575 break;
580 // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now.
582 break;
585 break;
588 break;
591 break;
594 break;
597 break;
598 case RTFKeyword::UPR:
600 break;
601 case RTFKeyword::UD:
602 // Anything inside \ud is just normal Unicode content.
604 break;
608 break;
610 {
611 RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart());
612 if (!aLookahead.hasTable())
613 {
615 m_xModelFactory->createInstance("com.sun.star.drawing.GroupShape"),
616 uno::UNO_QUERY);
618 uno::UNO_QUERY);
619 if (xDrawSupplier.is())
620 {
621 uno::Reference<drawing::XShape> xShape(xGroupShape, uno::UNO_QUERY);
622 // set default VertOrient before inserting
623 uno::Reference<beans::XPropertySet>(xShape, uno::UNO_QUERY_THROW)
624 ->setPropertyValue("VertOrient", uno::Any(text::VertOrientation::NONE));
625 xDrawSupplier->getDrawPage()->add(xShape);
626 }
627 m_pSdrImport->pushParent(xGroupShape);
629 }
632 }
633 break;
637 NS_ooxml::LN_CT_FtnEdn_type,
638 new RTFValue(NS_ooxml::LN_Value_doc_ST_FtnEdn_separator));
639 break;
641 // Container of all user-defined properties.
643 if (m_xDocumentProperties.is())
644 // Create a custom document properties to be able to process them later all at once.
645 m_xDocumentProperties = document::DocumentProperties::create(m_xContext);
646 break;
649 break;
652 break;
655 break;
656 default:
657 {
658 // Check if it's a math token.
659 RTFMathSymbol aSymbol(nKeyword);
661 {
664 return RTFError::OK;
665 }
666
667 SAL_INFO("writerfilter",
668 "TODO handle destination '" << keywordToString(nKeyword) << "'");
669 // Make sure we skip destinations (even without \*) till we don't handle them
671 aSkip.setParsed(false);
672 }
673 break;
674 }
675
676 // new destination => use new destination text
678
679 return RTFError::OK;
680}
681
682} // namespace writerfilter
683
684/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt64 Tell() const
SvStream & ReadChar(char &rChar)
sal_uInt64 Seek(sal_uInt64 nPos)
void appendOpeningTag(int token, const css::uno::Reference< css::xml::sax::XFastAttributeList > &attributes=css::uno::Reference< css::xml::sax::XFastAttributeList >())
virtual void endCharacterGroup()=0
Receives end mark for group with the same character properties.
virtual void props(writerfilter::Reference< Properties >::Pointer_t ref)=0
Receives properties of the current run of text.
virtual void startCharacterGroup()=0
Receives start mark for group with the same character properties.
void setNeedSect(bool bNeedSect)
If we need a final section break at the end of the document.
RTFDocumentImpl * m_pSuperstream
Superstream of this substream.
bool m_bObject
If we are in an object group and if the we use its \objdata element.
OUString m_aAuthor
Annotation author of the next annotation.
OUString m_aPicturePath
For the INCLUDEPICTURE field's argument.
RTFError dispatchDestination(RTFKeyword nKeyword) override
void resetFrame()
Resets m_aStates.top().aFrame.
std::queue< std::pair< Id, std::size_t > > m_nHeaderFooterPositions
bool m_bNeedCrOrig
Original value of m_bNeedCr – saved/restored before/after textframes.
css::uno::Reference< css::uno::XComponentContext > const & m_xContext
void singleChar(sal_uInt8 nValue, bool bRunProps=false)
std::deque< RTFSprms > m_aNestedTableCellsAttributes
void checkUnicode(bool bUnicode, bool bHex)
If we have some unicode or hex characters to send.
RTFBuffer_t m_aSuperBuffer
Buffered superscript, till footnote is reached (or not).
css::uno::Reference< css::lang::XMultiServiceFactory > m_xModelFactory
std::deque< RTFSprms > m_aNestedTableCellsSprms
int m_nNestedCells
cell props buffer for nested tables, reset by \nestrow the \nesttableprops is a destination and must ...
int m_nListLevel
Index of the current list level in a list table entry.
oox::formulaimport::XmlStreamBuilder m_aMathBuffer
Formula import.
OUString m_aAuthorInitials
Initials of author of the next annotation.
bool m_bNeedCr
If we need to emit a CR at the end of substream.
css::uno::Reference< css::lang::XComponent > const & m_xDstDoc
tools::SvRef< RTFTokenizer > m_pTokenizer
RTFParserState m_aDefaultState
Read by RTF_PARD.
void checkFirstRun()
If this is the first run of the document, starts the initial paragraph.
tools::SvRef< RTFSdrImport > m_pSdrImport
RTFError dispatchFlag(RTFKeyword nKeyword) override
void resolveSubstream(std::size_t nPos, Id nId)
bool m_bNeedPap
If paragraph properties should be emitted on next run.
bool m_hasRHeader
Flags for ensuring that only one header and footer is added per section.
css::uno::Reference< css::document::XDocumentProperties > m_xDocumentProperties
This acts like an importer, but used for looking ahead, e.g.
Represents an RTF Math Control Word.
void setCreatedShapeGroup(bool bCreatedShapeGroup)
void setCurrentBuffer(RTFBuffer_t *pCurrentBuffer)
void setDestination(Destination eDestination)
void setInShapeGroup(bool bInShapeGroup)
void setInListpicture(bool bInListpicture)
void setInBackground(bool bInBackground)
void setCurrentDestinationText(OUStringBuffer *pDestinationText)
Sends RTFSprm instances to DomainMapper.
std::vector< std::pair< OUString, OUString > > & getProperties()
Skips a destination after a not parsed control word if it was prefixed with *.
A list of RTFSprm with a copy constructor that performs a deep copy.
Definition: rtfsprm.hxx:39
void set(Id nKeyword, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite=RTFOverwrite::YES)
Does the same as ->push_back(), except that it can overwrite or ignore existing entries.
Definition: rtfsprm.cxx:98
static bool lookupMathKeyword(RTFMathSymbol &rSymbol)
To look up additional properties of a math symbol.
Value of an RTF keyword.
Definition: rtfvalue.hxx:33
#define ESCHER_ShpInst_PictureFrame
sal_uInt16 nPos
#define SAL_INFO(area, stream)
aBuf
int i
std::tuple< OUString, std::vector< OUString >, std::vector< OUString > > splitFieldCommand(std::u16string_view rCommand)
std::tuple< RTFBufferTypes, RTFValue::Pointer_t, tools::SvRef< TableRowBuffer > > Buf_t
A buffer storing dmapper calls.
const char * keywordToString(RTFKeyword nKeyword)
const sal_uInt8 cFieldStart
sal_Int16 nId
sal_uInt32 Id