1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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
37 RTFError
RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword
)
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);
54 m_aStates
.top().eDestination
= Destination::FONTTABLE
;
57 m_aStates
.top().eDestination
= Destination::COLORTABLE
;
60 m_aStates
.top().eDestination
= Destination::STYLESHEET
;
63 m_aStates
.top().eDestination
= Destination::FIELD
;
67 // Look for the field type
68 sal_uInt64
const nPos
= Strm().Tell();
71 bool bFoundCode
= false;
72 bool bInKeyword
= false;
73 while (!bFoundCode
&& ch
!= '}')
78 if (!bInKeyword
&& rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch
)))
80 else if (bInKeyword
&& rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch
)))
83 && !rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch
)))
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.
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
);
104 = std::get
<1>(aResult
).empty() ? OUString() : std::get
<1>(aResult
).front();
109 // Form data should be handled only for form fields if any
110 if (aBuf
.toString().indexOf(OString("FORM")) != -1)
113 singleChar(cFieldStart
);
114 m_aStates
.top().eDestination
= Destination::FIELDINSTRUCTION
;
118 m_aStates
.top().eDestination
= Destination::FIELDRESULT
;
121 m_aStates
.top().eDestination
= Destination::LISTTABLE
;
123 case RTF_LISTPICTURE
:
124 m_aStates
.top().eDestination
= Destination::LISTPICTURE
;
125 m_aStates
.top().bInListpicture
= true;
128 m_aStates
.top().eDestination
= Destination::LISTENTRY
;
131 m_aStates
.top().eDestination
= Destination::LISTNAME
;
134 m_aStates
.top().eDestination
= Destination::LFOLEVEL
;
135 m_aStates
.top().aTableSprms
.clear();
137 case RTF_LISTOVERRIDETABLE
:
138 m_aStates
.top().eDestination
= Destination::LISTOVERRIDETABLE
;
140 case RTF_LISTOVERRIDE
:
141 m_aStates
.top().eDestination
= Destination::LISTOVERRIDEENTRY
;
144 m_aStates
.top().eDestination
= Destination::LISTLEVEL
;
148 m_aStates
.top().eDestination
= Destination::LEVELTEXT
;
150 case RTF_LEVELNUMBERS
:
151 m_aStates
.top().eDestination
= Destination::LEVELNUMBERS
;
155 m_aStates
.top().eDestination
= Destination::SHPPICT
;
158 if (m_aStates
.top().eDestination
!= Destination::SHAPEPROPERTYVALUE
)
159 m_aStates
.top().eDestination
= Destination::PICT
; // as character
161 m_aStates
.top().eDestination
162 = Destination::SHAPEPROPERTYVALUEPICT
; // anchored inside a shape
165 m_aStates
.top().eDestination
= Destination::PICPROP
;
168 m_aStates
.top().eDestination
= Destination::SHAPEPROPERTY
;
171 m_aStates
.top().eDestination
= Destination::SHAPEPROPERTYNAME
;
174 m_aStates
.top().eDestination
= Destination::SHAPEPROPERTYVALUE
;
177 m_bNeedCrOrig
= m_bNeedCr
;
178 m_aStates
.top().eDestination
= Destination::SHAPE
;
179 m_aStates
.top().bInShape
= true;
182 m_aStates
.top().eDestination
= Destination::SHAPEINSTRUCTION
;
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();
191 m_aStates
.top().eDestination
= Destination::NESTEDTABLEPROPERTIES
;
204 std::size_t nPos
= m_nGroupStartPos
- 1;
210 nId
= NS_ooxml::LN_headerr
;
217 nId
= NS_ooxml::LN_footerr
;
222 nId
= NS_ooxml::LN_headerl
;
225 nId
= NS_ooxml::LN_headerr
;
230 nId
= NS_ooxml::LN_headerf
;
235 nId
= NS_ooxml::LN_footerl
;
238 nId
= NS_ooxml::LN_footerr
;
243 nId
= NS_ooxml::LN_footerf
;
252 m_nHeaderFooterPositions
.push(std::make_pair(nId
, nPos
));
254 m_aStates
.top().eDestination
= Destination::SKIP
;
261 Id nId
= NS_ooxml::LN_footnote
;
263 // Check if this is an endnote.
266 sal_uInt64
const nCurrent
= Strm().Tell();
267 for (int i
= 0; i
< 7; ++i
)
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();
289 m_aSuperBuffer
.clear();
290 m_aStates
.top().eDestination
= Destination::FOOTNOTE
;
291 Mapper().startCharacterGroup();
293 if (!m_aStates
.top().pCurrentBuffer
)
294 resolveSubstream(m_nGroupStartPos
- 1, nId
, aCustomMark
);
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));
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
);
313 Mapper().endCharacterGroup();
314 m_aStates
.top().eDestination
= Destination::SKIP
;
318 m_aStates
.top().eDestination
= Destination::BOOKMARKSTART
;
321 m_aStates
.top().eDestination
= Destination::BOOKMARKEND
;
324 m_aStates
.top().eDestination
= Destination::INDEXENTRY
;
328 m_aStates
.top().eDestination
= Destination::TOCENTRY
;
331 m_aStates
.top().eDestination
= Destination::REVISIONTABLE
;
336 resolveSubstream(m_nGroupStartPos
- 1, NS_ooxml::LN_annotation
);
337 m_aStates
.top().eDestination
= Destination::SKIP
;
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
);
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;
375 // Skip text on picture frames.
376 m_aStates
.top().eDestination
= Destination::SKIP
;
379 m_aStates
.top().eDestination
= Destination::SHAPETEXT
;
381 dispatchFlag(RTF_PARD
);
383 if (nKeyword
== RTF_SHPTXT
)
385 if (!m_aStates
.top().pCurrentBuffer
)
386 m_pSdrImport
->resolve(m_aStates
.top().aShape
, false,
387 RTFSdrImport::SHAPE
);
390 auto pValue
= new RTFValue(m_aStates
.top().aShape
);
391 m_aStates
.top().pCurrentBuffer
->push_back(
392 Buf_t(BUFFER_STARTSHAPE
, pValue
, nullptr));
399 if (m_aStates
.top().eDestination
== Destination::FIELDINSTRUCTION
)
400 m_aStates
.top().eDestination
= Destination::FORMFIELD
;
403 m_aStates
.top().eDestination
= Destination::FORMFIELDNAME
;
406 m_aStates
.top().eDestination
= Destination::FORMFIELDLIST
;
409 m_aStates
.top().eDestination
= Destination::DATAFIELD
;
412 m_aStates
.top().eDestination
= Destination::INFO
;
415 m_aStates
.top().eDestination
= Destination::CREATIONTIME
;
418 m_aStates
.top().eDestination
= Destination::REVISIONTIME
;
421 m_aStates
.top().eDestination
= Destination::PRINTTIME
;
424 m_aStates
.top().eDestination
= Destination::AUTHOR
;
427 m_aStates
.top().eDestination
= Destination::KEYWORDS
;
430 m_aStates
.top().eDestination
= Destination::OPERATOR
;
433 m_aStates
.top().eDestination
= Destination::COMPANY
;
436 m_aStates
.top().eDestination
= Destination::COMMENT
;
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.
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,
461 m_aStates
.top().eDestination
= Destination::SKIP
;
465 m_aStates
.top().eDestination
= Destination::OBJDATA
;
469 m_aStates
.top().eDestination
= Destination::OBJCLASS
;
472 m_aStates
.top().eDestination
= Destination::RESULT
;
475 m_aStates
.top().eDestination
= Destination::ANNOTATIONDATE
;
478 m_aStates
.top().eDestination
= Destination::ANNOTATIONAUTHOR
;
481 m_aStates
.top().eDestination
= Destination::ANNOTATIONREFERENCE
;
484 m_aStates
.top().eDestination
= Destination::FALT
;
487 m_aStates
.top().eDestination
= Destination::FLYMAINCONTENT
;
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
;
496 m_aStates
.top().eDestination
= Destination::DRAWINGOBJECT
;
499 m_aStates
.top().eDestination
= Destination::PARAGRAPHNUMBERING
;
502 // This destination should be ignored by readers that support paragraph numbering.
503 m_aStates
.top().eDestination
= Destination::SKIP
;
506 m_aStates
.top().eDestination
= Destination::PARAGRAPHNUMBERING_TEXTAFTER
;
509 m_aStates
.top().eDestination
= Destination::PARAGRAPHNUMBERING_TEXTBEFORE
;
512 m_aStates
.top().eDestination
= Destination::TITLE
;
515 m_aStates
.top().eDestination
= Destination::SUBJECT
;
518 m_aStates
.top().eDestination
= Destination::DOCCOMM
;
521 m_aStates
.top().eDestination
= Destination::ANNOTATIONREFERENCESTART
;
524 m_aStates
.top().eDestination
= Destination::ANNOTATIONREFERENCEEND
;
527 m_aStates
.top().eDestination
= Destination::ATNID
;
531 // Nothing to do here (just enter the destination) till RTF_MMATHPR is implemented.
534 m_aStates
.top().eDestination
= Destination::MR
;
537 m_aStates
.top().eDestination
= Destination::MCHR
;
540 m_aStates
.top().eDestination
= Destination::MPOS
;
543 m_aStates
.top().eDestination
= Destination::MVERTJC
;
546 m_aStates
.top().eDestination
= Destination::MSTRIKEH
;
549 m_aStates
.top().eDestination
= Destination::MDEGHIDE
;
552 m_aStates
.top().eDestination
= Destination::MTYPE
;
555 m_aStates
.top().eDestination
= Destination::MGROW
;
561 // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now.
562 m_aStates
.top().eDestination
= Destination::SKIP
;
565 m_aStates
.top().eDestination
= Destination::MSUBHIDE
;
568 m_aStates
.top().eDestination
= Destination::MSUPHIDE
;
571 m_aStates
.top().eDestination
= Destination::MBEGCHR
;
574 m_aStates
.top().eDestination
= Destination::MSEPCHR
;
577 m_aStates
.top().eDestination
= Destination::MENDCHR
;
580 m_aStates
.top().eDestination
= Destination::UPR
;
583 // Anything inside \ud is just normal Unicode content.
584 m_aStates
.top().eDestination
= Destination::NORMAL
;
587 m_aStates
.top().eDestination
= Destination::BACKGROUND
;
588 m_aStates
.top().bInBackground
= true;
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"),
598 uno::Reference
<drawing::XDrawPageSupplier
> xDrawSupplier(m_xDstDoc
,
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;
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
));
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
);
630 m_aStates
.top().eDestination
= Destination::PROPNAME
;
633 m_aStates
.top().eDestination
= Destination::STATICVAL
;
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
;
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);
656 // new destination => use new destination text
657 m_aStates
.top().pDestinationText
= &m_aStates
.top().aDestinationText
;
662 } // namespace rtftok
663 } // namespace writerfilter
665 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */