/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "rtfdocumentimpl.hxx" #include #include #include #include #include #include #include #include #include #include #include "rtflookahead.hxx" #include "rtfreferenceproperties.hxx" #include "rtfsdrimport.hxx" #include "rtfskipdestination.hxx" #include "rtftokenizer.hxx" using namespace com::sun::star; namespace writerfilter { namespace rtftok { RTFError RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword) { setNeedSect(true); checkUnicode(/*bUnicode =*/true, /*bHex =*/true); RTFSkipDestination aSkip(*this); // special case \upr: ignore everything except nested \ud if (Destination::UPR == m_aStates.top().eDestination && RTF_UD != nKeyword) { m_aStates.top().eDestination = Destination::SKIP; aSkip.setParsed(false); } else switch (nKeyword) { case RTF_RTF: break; case RTF_FONTTBL: m_aStates.top().eDestination = Destination::FONTTABLE; break; case RTF_COLORTBL: m_aStates.top().eDestination = Destination::COLORTABLE; break; case RTF_STYLESHEET: m_aStates.top().eDestination = Destination::STYLESHEET; break; case RTF_FIELD: m_aStates.top().eDestination = Destination::FIELD; break; case RTF_FLDINST: { // Look for the field type sal_uInt64 const nPos = Strm().Tell(); OStringBuffer aBuf; char ch = 0; bool bFoundCode = false; bool bInKeyword = false; while (!bFoundCode && ch != '}') { Strm().ReadChar(ch); if ('\\' == ch) bInKeyword = true; if (!bInKeyword && rtl::isAsciiAlphanumeric(static_cast(ch))) aBuf.append(ch); else if (bInKeyword && rtl::isAsciiWhiteSpace(static_cast(ch))) bInKeyword = false; if (!aBuf.isEmpty() && !rtl::isAsciiAlphanumeric(static_cast(ch))) bFoundCode = true; } if (aBuf.toString() == "INCLUDEPICTURE") { // Extract the field argument of INCLUDEPICTURE: we handle that // at a tokenizer level, as DOCX has no such field. aBuf.append(ch); while (true) { Strm().ReadChar(ch); if (ch == '}') break; aBuf.append(ch); } OUString aFieldCommand = OStringToOUString(aBuf.toString(), RTL_TEXTENCODING_UTF8); std::tuple, std::vector> aResult = writerfilter::dmapper::splitFieldCommand(aFieldCommand); m_aPicturePath = std::get<1>(aResult).empty() ? OUString() : std::get<1>(aResult).front(); } Strm().Seek(nPos); // Form data should be handled only for form fields if any if (aBuf.toString().indexOf(OString("FORM")) != -1) m_bFormField = true; singleChar(cFieldStart); m_aStates.top().eDestination = Destination::FIELDINSTRUCTION; } break; case RTF_FLDRSLT: m_aStates.top().eDestination = Destination::FIELDRESULT; break; case RTF_LISTTABLE: m_aStates.top().eDestination = Destination::LISTTABLE; break; case RTF_LISTPICTURE: m_aStates.top().eDestination = Destination::LISTPICTURE; m_aStates.top().bInListpicture = true; break; case RTF_LIST: m_aStates.top().eDestination = Destination::LISTENTRY; break; case RTF_LISTNAME: m_aStates.top().eDestination = Destination::LISTNAME; break; case RTF_LFOLEVEL: m_aStates.top().eDestination = Destination::LFOLEVEL; m_aStates.top().aTableSprms.clear(); break; case RTF_LISTOVERRIDETABLE: m_aStates.top().eDestination = Destination::LISTOVERRIDETABLE; break; case RTF_LISTOVERRIDE: m_aStates.top().eDestination = Destination::LISTOVERRIDEENTRY; break; case RTF_LISTLEVEL: m_aStates.top().eDestination = Destination::LISTLEVEL; ++m_nListLevel; break; case RTF_LEVELTEXT: m_aStates.top().eDestination = Destination::LEVELTEXT; break; case RTF_LEVELNUMBERS: m_aStates.top().eDestination = Destination::LEVELNUMBERS; break; case RTF_SHPPICT: resetFrame(); m_aStates.top().eDestination = Destination::SHPPICT; break; case RTF_PICT: if (m_aStates.top().eDestination != Destination::SHAPEPROPERTYVALUE) m_aStates.top().eDestination = Destination::PICT; // as character else m_aStates.top().eDestination = Destination::SHAPEPROPERTYVALUEPICT; // anchored inside a shape break; case RTF_PICPROP: m_aStates.top().eDestination = Destination::PICPROP; break; case RTF_SP: m_aStates.top().eDestination = Destination::SHAPEPROPERTY; break; case RTF_SN: m_aStates.top().eDestination = Destination::SHAPEPROPERTYNAME; break; case RTF_SV: m_aStates.top().eDestination = Destination::SHAPEPROPERTYVALUE; break; case RTF_SHP: m_bNeedCrOrig = m_bNeedCr; m_aStates.top().eDestination = Destination::SHAPE; m_aStates.top().bInShape = true; break; case RTF_SHPINST: m_aStates.top().eDestination = Destination::SHAPEINSTRUCTION; break; case RTF_NESTTABLEPROPS: // do not set any properties of outer table at nested table! m_aStates.top().aTableCellSprms = m_aDefaultState.aTableCellSprms; m_aStates.top().aTableCellAttributes = m_aDefaultState.aTableCellAttributes; m_aNestedTableCellsSprms.clear(); m_aNestedTableCellsAttributes.clear(); m_nNestedCells = 0; m_aStates.top().eDestination = Destination::NESTEDTABLEPROPERTIES; break; case RTF_HEADER: case RTF_FOOTER: case RTF_HEADERL: case RTF_HEADERR: case RTF_HEADERF: case RTF_FOOTERL: case RTF_FOOTERR: case RTF_FOOTERF: if (!m_pSuperstream) { Id nId = 0; std::size_t nPos = m_nGroupStartPos - 1; switch (nKeyword) { case RTF_HEADER: if (!m_hasRHeader) { nId = NS_ooxml::LN_headerr; m_hasRHeader = true; } break; case RTF_FOOTER: if (!m_hasRFooter) { nId = NS_ooxml::LN_footerr; m_hasRFooter = true; } break; case RTF_HEADERL: nId = NS_ooxml::LN_headerl; break; case RTF_HEADERR: nId = NS_ooxml::LN_headerr; break; case RTF_HEADERF: if (!m_hasFHeader) { nId = NS_ooxml::LN_headerf; m_hasFHeader = true; } break; case RTF_FOOTERL: nId = NS_ooxml::LN_footerl; break; case RTF_FOOTERR: nId = NS_ooxml::LN_footerr; break; case RTF_FOOTERF: if (!m_hasFFooter) { nId = NS_ooxml::LN_footerf; m_hasFFooter = true; } break; default: break; } if (nId != 0) m_nHeaderFooterPositions.push(std::make_pair(nId, nPos)); m_aStates.top().eDestination = Destination::SKIP; } break; case RTF_FOOTNOTE: checkFirstRun(); if (!m_pSuperstream) { Id nId = NS_ooxml::LN_footnote; // Check if this is an endnote. OStringBuffer aBuf; char ch; sal_uInt64 const nCurrent = Strm().Tell(); for (int i = 0; i < 7; ++i) { Strm().ReadChar(ch); aBuf.append(ch); } Strm().Seek(nCurrent); OString aKeyword = aBuf.makeStringAndClear(); if (aKeyword == "\\ftnalt") nId = NS_ooxml::LN_endnote; if (m_aStates.top().pCurrentBuffer == &m_aSuperBuffer) m_aStates.top().pCurrentBuffer = nullptr; bool bCustomMark = false; OUString aCustomMark; for (auto const& elem : m_aSuperBuffer) { if (std::get<0>(elem) == BUFFER_UTEXT) { aCustomMark = std::get<1>(elem)->getString(); bCustomMark = true; } } m_aSuperBuffer.clear(); m_aStates.top().eDestination = Destination::FOOTNOTE; Mapper().startCharacterGroup(); runProps(); if (!m_aStates.top().pCurrentBuffer) resolveSubstream(m_nGroupStartPos - 1, nId, aCustomMark); else { RTFSprms aAttributes; aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1)); aAttributes.set(Id(1), new RTFValue(nId)); aAttributes.set(Id(2), new RTFValue(aCustomMark)); m_aStates.top().pCurrentBuffer->push_back( Buf_t(BUFFER_RESOLVESUBSTREAM, new RTFValue(aAttributes), nullptr)); } if (bCustomMark) { m_aStates.top().aCharacterAttributes.clear(); m_aStates.top().aCharacterSprms.clear(); auto pValue = new RTFValue(1); m_aStates.top().aCharacterAttributes.set( NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows, pValue); text(aCustomMark); } Mapper().endCharacterGroup(); m_aStates.top().eDestination = Destination::SKIP; } break; case RTF_BKMKSTART: m_aStates.top().eDestination = Destination::BOOKMARKSTART; break; case RTF_BKMKEND: m_aStates.top().eDestination = Destination::BOOKMARKEND; break; case RTF_XE: m_aStates.top().eDestination = Destination::INDEXENTRY; break; case RTF_TC: case RTF_TCN: m_aStates.top().eDestination = Destination::TOCENTRY; break; case RTF_REVTBL: m_aStates.top().eDestination = Destination::REVISIONTABLE; break; case RTF_ANNOTATION: if (!m_pSuperstream) { resolveSubstream(m_nGroupStartPos - 1, NS_ooxml::LN_annotation); m_aStates.top().eDestination = Destination::SKIP; } else { // If there is an author set, emit it now. if (!m_aAuthor.isEmpty() || !m_aAuthorInitials.isEmpty()) { RTFSprms aAttributes; if (!m_aAuthor.isEmpty()) { auto pValue = new RTFValue(m_aAuthor); aAttributes.set(NS_ooxml::LN_CT_TrackChange_author, pValue); } if (!m_aAuthorInitials.isEmpty()) { auto pValue = new RTFValue(m_aAuthorInitials); aAttributes.set(NS_ooxml::LN_CT_Comment_initials, pValue); } writerfilter::Reference::Pointer_t pProperties = new RTFReferenceProperties(aAttributes); Mapper().props(pProperties); } } break; case RTF_SHPTXT: case RTF_DPTXBXTEXT: { bool bPictureFrame = false; for (auto& rProperty : m_aStates.top().aShape.aProperties) { if (rProperty.first == "shapeType" && rProperty.second == OUString::number(ESCHER_ShpInst_PictureFrame)) { bPictureFrame = true; break; } } if (bPictureFrame) // Skip text on picture frames. m_aStates.top().eDestination = Destination::SKIP; else { m_aStates.top().eDestination = Destination::SHAPETEXT; checkFirstRun(); dispatchFlag(RTF_PARD); m_bNeedPap = true; if (nKeyword == RTF_SHPTXT) { if (!m_aStates.top().pCurrentBuffer) m_pSdrImport->resolve(m_aStates.top().aShape, false, RTFSdrImport::SHAPE); else { auto pValue = new RTFValue(m_aStates.top().aShape); m_aStates.top().pCurrentBuffer->push_back( Buf_t(BUFFER_STARTSHAPE, pValue, nullptr)); } } } } break; case RTF_FORMFIELD: if (m_aStates.top().eDestination == Destination::FIELDINSTRUCTION) m_aStates.top().eDestination = Destination::FORMFIELD; break; case RTF_FFNAME: m_aStates.top().eDestination = Destination::FORMFIELDNAME; break; case RTF_FFL: m_aStates.top().eDestination = Destination::FORMFIELDLIST; break; case RTF_DATAFIELD: m_aStates.top().eDestination = Destination::DATAFIELD; break; case RTF_INFO: m_aStates.top().eDestination = Destination::INFO; break; case RTF_CREATIM: m_aStates.top().eDestination = Destination::CREATIONTIME; break; case RTF_REVTIM: m_aStates.top().eDestination = Destination::REVISIONTIME; break; case RTF_PRINTIM: m_aStates.top().eDestination = Destination::PRINTTIME; break; case RTF_AUTHOR: m_aStates.top().eDestination = Destination::AUTHOR; break; case RTF_KEYWORDS: m_aStates.top().eDestination = Destination::KEYWORDS; break; case RTF_OPERATOR: m_aStates.top().eDestination = Destination::OPERATOR; break; case RTF_COMPANY: m_aStates.top().eDestination = Destination::COMPANY; break; case RTF_COMMENT: m_aStates.top().eDestination = Destination::COMMENT; break; case RTF_OBJECT: { // beginning of an OLE Object m_aStates.top().eDestination = Destination::OBJECT; // check if the object is in a special container (e.g. a table) if (!m_aStates.top().pCurrentBuffer) { // the object is in a table or another container. // Don't try to treat it as an OLE object (fdo#53594). // Use the \result (RTF_RESULT) element of the object instead, // the result element contain picture representing the OLE Object. m_bObject = true; } } break; case RTF_OBJDATA: // check if the object is in a special container (e.g. a table) if (m_aStates.top().pCurrentBuffer) { // the object is in a table or another container. // Use the \result (RTF_RESULT) element of the object instead, // of the \objdata. m_aStates.top().eDestination = Destination::SKIP; } else { m_aStates.top().eDestination = Destination::OBJDATA; } break; case RTF_OBJCLASS: m_aStates.top().eDestination = Destination::OBJCLASS; break; case RTF_RESULT: m_aStates.top().eDestination = Destination::RESULT; break; case RTF_ATNDATE: m_aStates.top().eDestination = Destination::ANNOTATIONDATE; break; case RTF_ATNAUTHOR: m_aStates.top().eDestination = Destination::ANNOTATIONAUTHOR; break; case RTF_ATNREF: m_aStates.top().eDestination = Destination::ANNOTATIONREFERENCE; break; case RTF_FALT: m_aStates.top().eDestination = Destination::FALT; break; case RTF_FLYMAINCNT: m_aStates.top().eDestination = Destination::FLYMAINCONTENT; break; case RTF_LISTTEXT: // Should be ignored by any reader that understands Word 97 through Word 2007 numbering. case RTF_NONESTTABLES: // This destination should be ignored by readers that support nested tables. m_aStates.top().eDestination = Destination::SKIP; break; case RTF_DO: m_aStates.top().eDestination = Destination::DRAWINGOBJECT; break; case RTF_PN: m_aStates.top().eDestination = Destination::PARAGRAPHNUMBERING; break; case RTF_PNTEXT: // This destination should be ignored by readers that support paragraph numbering. m_aStates.top().eDestination = Destination::SKIP; break; case RTF_PNTXTA: m_aStates.top().eDestination = Destination::PARAGRAPHNUMBERING_TEXTAFTER; break; case RTF_PNTXTB: m_aStates.top().eDestination = Destination::PARAGRAPHNUMBERING_TEXTBEFORE; break; case RTF_TITLE: m_aStates.top().eDestination = Destination::TITLE; break; case RTF_SUBJECT: m_aStates.top().eDestination = Destination::SUBJECT; break; case RTF_DOCCOMM: m_aStates.top().eDestination = Destination::DOCCOMM; break; case RTF_ATRFSTART: m_aStates.top().eDestination = Destination::ANNOTATIONREFERENCESTART; break; case RTF_ATRFEND: m_aStates.top().eDestination = Destination::ANNOTATIONREFERENCEEND; break; case RTF_ATNID: m_aStates.top().eDestination = Destination::ATNID; break; case RTF_MMATH: case RTF_MOMATHPARA: // Nothing to do here (just enter the destination) till RTF_MMATHPR is implemented. break; case RTF_MR: m_aStates.top().eDestination = Destination::MR; break; case RTF_MCHR: m_aStates.top().eDestination = Destination::MCHR; break; case RTF_MPOS: m_aStates.top().eDestination = Destination::MPOS; break; case RTF_MVERTJC: m_aStates.top().eDestination = Destination::MVERTJC; break; case RTF_MSTRIKEH: m_aStates.top().eDestination = Destination::MSTRIKEH; break; case RTF_MDEGHIDE: m_aStates.top().eDestination = Destination::MDEGHIDE; break; case RTF_MTYPE: m_aStates.top().eDestination = Destination::MTYPE; break; case RTF_MGROW: m_aStates.top().eDestination = Destination::MGROW; break; case RTF_MHIDETOP: case RTF_MHIDEBOT: case RTF_MHIDELEFT: case RTF_MHIDERIGHT: // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now. m_aStates.top().eDestination = Destination::SKIP; break; case RTF_MSUBHIDE: m_aStates.top().eDestination = Destination::MSUBHIDE; break; case RTF_MSUPHIDE: m_aStates.top().eDestination = Destination::MSUPHIDE; break; case RTF_MBEGCHR: m_aStates.top().eDestination = Destination::MBEGCHR; break; case RTF_MSEPCHR: m_aStates.top().eDestination = Destination::MSEPCHR; break; case RTF_MENDCHR: m_aStates.top().eDestination = Destination::MENDCHR; break; case RTF_UPR: m_aStates.top().eDestination = Destination::UPR; break; case RTF_UD: // Anything inside \ud is just normal Unicode content. m_aStates.top().eDestination = Destination::NORMAL; break; case RTF_BACKGROUND: m_aStates.top().eDestination = Destination::BACKGROUND; m_aStates.top().bInBackground = true; break; case RTF_SHPGRP: { RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart()); if (!aLookahead.hasTable()) { uno::Reference xGroupShape( m_xModelFactory->createInstance("com.sun.star.drawing.GroupShape"), uno::UNO_QUERY); uno::Reference xDrawSupplier(m_xDstDoc, uno::UNO_QUERY); if (xDrawSupplier.is()) { uno::Reference xShape(xGroupShape, uno::UNO_QUERY); // set default VertOrient before inserting uno::Reference(xShape, uno::UNO_QUERY) ->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::NONE)); xDrawSupplier->getDrawPage()->add(xShape); } m_pSdrImport->pushParent(xGroupShape); m_aStates.top().bCreatedShapeGroup = true; } m_aStates.top().eDestination = Destination::SHAPEGROUP; m_aStates.top().bInShapeGroup = true; } break; case RTF_FTNSEP: m_aStates.top().eDestination = Destination::FOOTNOTESEPARATOR; m_aStates.top().aCharacterAttributes.set( NS_ooxml::LN_CT_FtnEdn_type, new RTFValue(NS_ooxml::LN_Value_doc_ST_FtnEdn_separator)); break; case RTF_USERPROPS: // Container of all user-defined properties. m_aStates.top().eDestination = Destination::USERPROPS; if (m_xDocumentProperties.is()) // Create a custom document properties to be able to process them later all at once. m_xDocumentProperties = document::DocumentProperties::create(m_xContext); break; case RTF_PROPNAME: m_aStates.top().eDestination = Destination::PROPNAME; break; case RTF_STATICVAL: m_aStates.top().eDestination = Destination::STATICVAL; break; default: { // Check if it's a math token. RTFMathSymbol aSymbol; aSymbol.eKeyword = nKeyword; if (RTFTokenizer::lookupMathKeyword(aSymbol)) { m_aMathBuffer.appendOpeningTag(aSymbol.nToken); m_aStates.top().eDestination = aSymbol.eDestination; return RTFError::OK; } SAL_INFO("writerfilter", "TODO handle destination '" << keywordToString(nKeyword) << "'"); // Make sure we skip destinations (even without \*) till we don't handle them m_aStates.top().eDestination = Destination::SKIP; aSkip.setParsed(false); } break; } // new destination => use new destination text m_aStates.top().pDestinationText = &m_aStates.top().aDestinationText; return RTFError::OK; } } // namespace rtftok } // namespace writerfilter /* vim:set shiftwidth=4 softtabstop=4 expandtab: */