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().getDestination() && RTF_UD
!= nKeyword
)
45 m_aStates
.top().setDestination(Destination::SKIP
);
46 aSkip
.setParsed(false);
54 m_aStates
.top().setDestination(Destination::FONTTABLE
);
57 m_aStates
.top().setDestination(Destination::COLORTABLE
);
60 m_aStates
.top().setDestination(Destination::STYLESHEET
);
63 m_aStates
.top().setDestination(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("FORM") != -1)
113 singleChar(cFieldStart
);
114 m_aStates
.top().setDestination(Destination::FIELDINSTRUCTION
);
118 m_aStates
.top().setDestination(Destination::FIELDRESULT
);
121 m_aStates
.top().setDestination(Destination::LISTTABLE
);
123 case RTF_LISTPICTURE
:
124 m_aStates
.top().setDestination(Destination::LISTPICTURE
);
125 m_aStates
.top().setInListpicture(true);
128 m_aStates
.top().setDestination(Destination::LISTENTRY
);
131 m_aStates
.top().setDestination(Destination::LISTNAME
);
134 m_aStates
.top().setDestination(Destination::LFOLEVEL
);
135 m_aStates
.top().getTableSprms().clear();
137 case RTF_LISTOVERRIDETABLE
:
138 m_aStates
.top().setDestination(Destination::LISTOVERRIDETABLE
);
140 case RTF_LISTOVERRIDE
:
141 m_aStates
.top().setDestination(Destination::LISTOVERRIDEENTRY
);
144 m_aStates
.top().setDestination(Destination::LISTLEVEL
);
148 m_aStates
.top().setDestination(Destination::LEVELTEXT
);
150 case RTF_LEVELNUMBERS
:
151 m_aStates
.top().setDestination(Destination::LEVELNUMBERS
);
155 m_aStates
.top().setDestination(Destination::SHPPICT
);
158 if (m_aStates
.top().getDestination() != Destination::SHAPEPROPERTYVALUE
)
159 m_aStates
.top().setDestination(Destination::PICT
); // as character
161 m_aStates
.top().setDestination(
162 Destination::SHAPEPROPERTYVALUEPICT
); // anchored inside a shape
165 m_aStates
.top().setDestination(Destination::PICPROP
);
168 m_aStates
.top().setDestination(Destination::SHAPEPROPERTY
);
171 m_aStates
.top().setDestination(Destination::SHAPEPROPERTYNAME
);
174 m_aStates
.top().setDestination(Destination::SHAPEPROPERTYVALUE
);
177 m_bNeedCrOrig
= m_bNeedCr
;
178 m_aStates
.top().setDestination(Destination::SHAPE
);
179 m_aStates
.top().setInShape(true);
182 m_aStates
.top().setDestination(Destination::SHAPEINSTRUCTION
);
184 case RTF_NESTTABLEPROPS
:
185 // do not set any properties of outer table at nested table!
186 m_aStates
.top().getTableCellSprms() = m_aDefaultState
.getTableCellSprms();
187 m_aStates
.top().getTableCellAttributes() = m_aDefaultState
.getTableCellAttributes();
188 m_aNestedTableCellsSprms
.clear();
189 m_aNestedTableCellsAttributes
.clear();
191 m_aStates
.top().setDestination(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().setDestination(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().getCurrentBuffer() == &m_aSuperBuffer
)
278 m_aStates
.top().setCurrentBuffer(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().setDestination(Destination::FOOTNOTE
);
291 Mapper().startCharacterGroup();
293 if (!m_aStates
.top().getCurrentBuffer())
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().getCurrentBuffer()->push_back(
302 Buf_t(BUFFER_RESOLVESUBSTREAM
, new RTFValue(aAttributes
), nullptr));
306 m_aStates
.top().getCharacterAttributes().clear();
307 m_aStates
.top().getCharacterSprms().clear();
308 auto pValue
= new RTFValue(1);
309 m_aStates
.top().getCharacterAttributes().set(
310 NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows
, pValue
);
313 Mapper().endCharacterGroup();
314 m_aStates
.top().setDestination(Destination::SKIP
);
318 m_aStates
.top().setDestination(Destination::BOOKMARKSTART
);
321 m_aStates
.top().setDestination(Destination::BOOKMARKEND
);
324 m_aStates
.top().setDestination(Destination::INDEXENTRY
);
328 m_aStates
.top().setDestination(Destination::TOCENTRY
);
331 m_aStates
.top().setDestination(Destination::REVISIONTABLE
);
336 resolveSubstream(m_nGroupStartPos
- 1, NS_ooxml::LN_annotation
);
337 m_aStates
.top().setDestination(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 (const auto& rProperty
: m_aStates
.top().getShape().getProperties())
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().setDestination(Destination::SKIP
);
379 m_aStates
.top().setDestination(Destination::SHAPETEXT
);
381 dispatchFlag(RTF_PARD
);
383 if (nKeyword
== RTF_SHPTXT
)
385 if (!m_aStates
.top().getCurrentBuffer())
386 m_pSdrImport
->resolve(m_aStates
.top().getShape(), false,
387 RTFSdrImport::SHAPE
);
390 auto pValue
= new RTFValue(m_aStates
.top().getShape());
391 m_aStates
.top().getCurrentBuffer()->push_back(
392 Buf_t(BUFFER_STARTSHAPE
, pValue
, nullptr));
399 if (m_aStates
.top().getDestination() == Destination::FIELDINSTRUCTION
)
400 m_aStates
.top().setDestination(Destination::FORMFIELD
);
403 m_aStates
.top().setDestination(Destination::FORMFIELDNAME
);
406 m_aStates
.top().setDestination(Destination::FORMFIELDLIST
);
409 m_aStates
.top().setDestination(Destination::DATAFIELD
);
412 m_aStates
.top().setDestination(Destination::INFO
);
415 m_aStates
.top().setDestination(Destination::CREATIONTIME
);
418 m_aStates
.top().setDestination(Destination::REVISIONTIME
);
421 m_aStates
.top().setDestination(Destination::PRINTTIME
);
424 m_aStates
.top().setDestination(Destination::AUTHOR
);
427 m_aStates
.top().setDestination(Destination::KEYWORDS
);
430 m_aStates
.top().setDestination(Destination::OPERATOR
);
433 m_aStates
.top().setDestination(Destination::COMPANY
);
436 m_aStates
.top().setDestination(Destination::COMMENT
);
440 // beginning of an OLE Object
441 m_aStates
.top().setDestination(Destination::OBJECT
);
443 // check if the object is in a special container (e.g. a table)
444 if (!m_aStates
.top().getCurrentBuffer())
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().getCurrentBuffer())
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().setDestination(Destination::SKIP
);
465 m_aStates
.top().setDestination(Destination::OBJDATA
);
469 m_aStates
.top().setDestination(Destination::OBJCLASS
);
472 m_aStates
.top().setDestination(Destination::RESULT
);
475 m_aStates
.top().setDestination(Destination::ANNOTATIONDATE
);
478 m_aStates
.top().setDestination(Destination::ANNOTATIONAUTHOR
);
481 m_aStates
.top().setDestination(Destination::ANNOTATIONREFERENCE
);
484 m_aStates
.top().setDestination(Destination::FALT
);
487 m_aStates
.top().setDestination(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().setDestination(Destination::SKIP
);
496 m_aStates
.top().setDestination(Destination::DRAWINGOBJECT
);
499 m_aStates
.top().setDestination(Destination::PARAGRAPHNUMBERING
);
502 // This destination should be ignored by readers that support paragraph numbering.
503 m_aStates
.top().setDestination(Destination::SKIP
);
506 m_aStates
.top().setDestination(Destination::PARAGRAPHNUMBERING_TEXTAFTER
);
509 m_aStates
.top().setDestination(Destination::PARAGRAPHNUMBERING_TEXTBEFORE
);
512 m_aStates
.top().setDestination(Destination::TITLE
);
515 m_aStates
.top().setDestination(Destination::SUBJECT
);
518 m_aStates
.top().setDestination(Destination::DOCCOMM
);
521 m_aStates
.top().setDestination(Destination::ANNOTATIONREFERENCESTART
);
524 m_aStates
.top().setDestination(Destination::ANNOTATIONREFERENCEEND
);
527 m_aStates
.top().setDestination(Destination::ATNID
);
531 // Nothing to do here (just enter the destination) till RTF_MMATHPR is implemented.
534 m_aStates
.top().setDestination(Destination::MR
);
537 m_aStates
.top().setDestination(Destination::MCHR
);
540 m_aStates
.top().setDestination(Destination::MPOS
);
543 m_aStates
.top().setDestination(Destination::MVERTJC
);
546 m_aStates
.top().setDestination(Destination::MSTRIKEH
);
549 m_aStates
.top().setDestination(Destination::MDEGHIDE
);
552 m_aStates
.top().setDestination(Destination::MTYPE
);
555 m_aStates
.top().setDestination(Destination::MGROW
);
561 // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now.
562 m_aStates
.top().setDestination(Destination::SKIP
);
565 m_aStates
.top().setDestination(Destination::MSUBHIDE
);
568 m_aStates
.top().setDestination(Destination::MSUPHIDE
);
571 m_aStates
.top().setDestination(Destination::MBEGCHR
);
574 m_aStates
.top().setDestination(Destination::MSEPCHR
);
577 m_aStates
.top().setDestination(Destination::MENDCHR
);
580 m_aStates
.top().setDestination(Destination::UPR
);
583 // Anything inside \ud is just normal Unicode content.
584 m_aStates
.top().setDestination(Destination::NORMAL
);
587 m_aStates
.top().setDestination(Destination::BACKGROUND
);
588 m_aStates
.top().setInBackground(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_THROW
)
605 ->setPropertyValue("VertOrient",
606 uno::makeAny(text::VertOrientation::NONE
));
607 xDrawSupplier
->getDrawPage()->add(xShape
);
609 m_pSdrImport
->pushParent(xGroupShape
);
610 m_aStates
.top().setCreatedShapeGroup(true);
612 m_aStates
.top().setDestination(Destination::SHAPEGROUP
);
613 m_aStates
.top().setInShapeGroup(true);
617 m_aStates
.top().setDestination(Destination::FOOTNOTESEPARATOR
);
618 m_aStates
.top().getCharacterAttributes().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().setDestination(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().setDestination(Destination::PROPNAME
);
633 m_aStates
.top().setDestination(Destination::STATICVAL
);
636 m_aStates
.top().setDestination(Destination::GENERATOR
);
640 // Check if it's a math token.
641 RTFMathSymbol
aSymbol(nKeyword
);
642 if (RTFTokenizer::lookupMathKeyword(aSymbol
))
644 m_aMathBuffer
.appendOpeningTag(aSymbol
.GetToken());
645 m_aStates
.top().setDestination(aSymbol
.GetDestination());
649 SAL_INFO("writerfilter",
650 "TODO handle destination '" << keywordToString(nKeyword
) << "'");
651 // Make sure we skip destinations (even without \*) till we don't handle them
652 m_aStates
.top().setDestination(Destination::SKIP
);
653 aSkip
.setParsed(false);
658 // new destination => use new destination text
659 m_aStates
.top().setCurrentDestinationText(&m_aStates
.top().getDestinationText());
664 } // namespace rtftok
665 } // namespace writerfilter
667 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */