[Libreoffice-commits] core.git: writerfilter/Library_writerfilter.mk writerfilter/source

Miklos Vajna vmiklos at collabora.co.uk
Mon May 9 15:29:33 UTC 2016


 writerfilter/Library_writerfilter.mk                  |    4 
 writerfilter/source/rtftok/rtfcontrolwords.hxx        |    1 
 writerfilter/source/rtftok/rtfdispatchdestination.cxx |  623 +++
 writerfilter/source/rtftok/rtfdispatchflag.cxx        | 1083 +++++
 writerfilter/source/rtftok/rtfdispatchsymbol.cxx      |  409 ++
 writerfilter/source/rtftok/rtfdispatchvalue.cxx       | 1347 +++++++
 writerfilter/source/rtftok/rtfdocumentimpl.cxx        | 3411 ------------------
 writerfilter/source/rtftok/rtfdocumentimpl.hxx        |   12 
 writerfilter/source/rtftok/rtffly.hxx                 |    4 
 9 files changed, 3522 insertions(+), 3372 deletions(-)

New commits:
commit a3615d5517ee84ddf9a9b2c28ff6a3a37fcb70db
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Mon May 9 09:13:25 2016 +0200

    writerfilter: extract dispatch{destination,flag,symbol,value} from rtfdocimpl
    
    These were half of the lines of rtfdocumentimpl.
    
    Change-Id: I3f24cd5d23c91bf0d53b898266c187699ae6ee56
    Reviewed-on: https://gerrit.libreoffice.org/24790
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Jenkins <ci at libreoffice.org>

diff --git a/writerfilter/Library_writerfilter.mk b/writerfilter/Library_writerfilter.mk
index 27eb752..b23256e 100644
--- a/writerfilter/Library_writerfilter.mk
+++ b/writerfilter/Library_writerfilter.mk
@@ -64,6 +64,10 @@ $(eval $(call gb_Library_use_externals,writerfilter,\
 $(eval $(call gb_Library_add_exception_objects,writerfilter,\
 	writerfilter/source/rtftok/rtfcharsets \
 	writerfilter/source/rtftok/rtfcontrolwords \
+	writerfilter/source/rtftok/rtfdispatchdestination \
+	writerfilter/source/rtftok/rtfdispatchflag \
+	writerfilter/source/rtftok/rtfdispatchsymbol \
+	writerfilter/source/rtftok/rtfdispatchvalue \
 	writerfilter/source/rtftok/rtfdocumentfactory \
 	writerfilter/source/rtftok/rtfdocumentimpl \
 	writerfilter/source/rtftok/rtflookahead \
diff --git a/writerfilter/source/rtftok/rtfcontrolwords.hxx b/writerfilter/source/rtftok/rtfcontrolwords.hxx
index 80a2d1e..2a7d596 100644
--- a/writerfilter/source/rtftok/rtfcontrolwords.hxx
+++ b/writerfilter/source/rtftok/rtfcontrolwords.hxx
@@ -1980,6 +1980,7 @@ enum RTFKeyword
     RTF_FLYHORZ,
     RTF_FLYANCHOR
 };
+const char* keywordToString(RTFKeyword nKeyword);
 
 /// Types of an RTF Control Word
 enum RTFControlTypes
diff --git a/writerfilter/source/rtftok/rtfdispatchdestination.cxx b/writerfilter/source/rtftok/rtfdispatchdestination.cxx
new file mode 100644
index 0000000..af8a3f1
--- /dev/null
+++ b/writerfilter/source/rtftok/rtfdispatchdestination.cxx
@@ -0,0 +1,623 @@
+/* -*- 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 <com/sun/star/document/DocumentProperties.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+
+#include <filter/msfilter/escherex.hxx>
+#include <tools/stream.hxx>
+
+#include <dmapper/DomainMapperFactory.hxx>
+#include <ooxml/resourceids.hxx>
+
+#include <rtflookahead.hxx>
+#include <rtfreferenceproperties.hxx>
+#include <rtfsdrimport.hxx>
+#include <rtfskipdestination.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_Size 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  && isalnum(ch))
+                    aBuf.append(ch);
+                else if (bInKeyword && isspace(ch))
+                    bInKeyword = false;
+                if (!aBuf.isEmpty() && !isalnum(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<OUString, std::vector<OUString>, std::vector<OUString> > 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;
+            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;
+                sal_Size nPos = m_nGroupStartPos - 1;
+                switch (nKeyword)
+                {
+                case RTF_HEADER:
+                    nId = NS_ooxml::LN_headerr;
+                    break;
+                case RTF_FOOTER:
+                    nId = NS_ooxml::LN_footerr;
+                    break;
+                case RTF_HEADERL:
+                    nId = NS_ooxml::LN_headerl;
+                    break;
+                case RTF_HEADERR:
+                    nId = NS_ooxml::LN_headerr;
+                    break;
+                case RTF_HEADERF:
+                    nId = NS_ooxml::LN_headerf;
+                    break;
+                case RTF_FOOTERL:
+                    nId = NS_ooxml::LN_footerl;
+                    break;
+                case RTF_FOOTERR:
+                    nId = NS_ooxml::LN_footerr;
+                    break;
+                case RTF_FOOTERF:
+                    nId = NS_ooxml::LN_footerf;
+                    break;
+                default:
+                    break;
+                }
+                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_Size 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.equals("\\ftnalt"))
+                    nId = NS_ooxml::LN_endnote;
+
+                if (m_aStates.top().pCurrentBuffer == &m_aSuperBuffer)
+                    m_aStates.top().pCurrentBuffer = nullptr;
+                bool bCustomMark = false;
+                OUString aCustomMark;
+                while (m_aSuperBuffer.size())
+                {
+                    Buf_t aTuple = m_aSuperBuffer.front();
+                    m_aSuperBuffer.pop_front();
+                    if (std::get<0>(aTuple) == BUFFER_UTEXT)
+                    {
+                        aCustomMark = std::get<1>(aTuple)->getString();
+                        bCustomMark = true;
+                    }
+                }
+                m_aStates.top().eDestination = Destination::FOOTNOTE;
+                if (bCustomMark)
+                    Mapper().startCharacterGroup();
+                if (!m_aStates.top().pCurrentBuffer)
+                    resolveSubstream(m_nGroupStartPos - 1, nId, aCustomMark);
+                else
+                {
+                    RTFSprms aAttributes;
+                    aAttributes.set(Id(0), std::make_shared<RTFValue>(m_nGroupStartPos - 1));
+                    aAttributes.set(Id(1), std::make_shared<RTFValue>(nId));
+                    aAttributes.set(Id(2), std::make_shared<RTFValue>(aCustomMark));
+                    m_aStates.top().pCurrentBuffer->push_back(Buf_t(BUFFER_RESOLVESUBSTREAM, std::make_shared<RTFValue>(aAttributes), nullptr));
+                }
+                if (bCustomMark)
+                {
+                    m_aStates.top().aCharacterAttributes.clear();
+                    m_aStates.top().aCharacterSprms.clear();
+                    auto pValue = std::make_shared<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 = std::make_shared<RTFValue>(m_aAuthor);
+                        aAttributes.set(NS_ooxml::LN_CT_TrackChange_author, pValue);
+                    }
+                    if (!m_aAuthorInitials.isEmpty())
+                    {
+                        auto pValue = std::make_shared<RTFValue>(m_aAuthorInitials);
+                        aAttributes.set(NS_ooxml::LN_CT_Comment_initials, pValue);
+                    }
+                    writerfilter::Reference<Properties>::Pointer_t pProperties = std::make_shared<RTFReferenceProperties>(aAttributes);
+                    Mapper().props(pProperties);
+                }
+            }
+            break;
+        case RTF_SHPTXT:
+        case RTF_DPTXBXTEXT:
+        {
+            bool bPictureFrame = false;
+            for (std::size_t i = 0; i < m_aStates.top().aShape.aProperties.size(); ++i)
+            {
+                std::pair<OUString, OUString>& rProperty = m_aStates.top().aShape.aProperties[i];
+                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 = std::make_shared<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<drawing::XShapes> xGroupShape(m_xModelFactory->createInstance("com.sun.star.drawing.GroupShape"), uno::UNO_QUERY);
+                uno::Reference<drawing::XDrawPageSupplier> xDrawSupplier(m_xDstDoc, uno::UNO_QUERY);
+                if (xDrawSupplier.is())
+                {
+                    uno::Reference<drawing::XShape> xShape(xGroupShape, uno::UNO_QUERY);
+                    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, std::make_shared<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: */
diff --git a/writerfilter/source/rtftok/rtfdispatchflag.cxx b/writerfilter/source/rtftok/rtfdispatchflag.cxx
new file mode 100644
index 0000000..d3bc517
--- /dev/null
+++ b/writerfilter/source/rtftok/rtfdispatchflag.cxx
@@ -0,0 +1,1083 @@
+/* -*- 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 <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+
+#include <filter/msfilter/escherex.hxx>
+
+#include <ooxml/resourceids.hxx>
+
+#include <rtfsdrimport.hxx>
+#include <rtfskipdestination.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter
+{
+namespace rtftok
+{
+
+RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword)
+{
+    setNeedSect(true);
+    checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
+    RTFSkipDestination aSkip(*this);
+    int nParam = -1;
+    int nSprm = -1;
+
+    // Underline flags.
+    switch (nKeyword)
+    {
+    case RTF_ULD:
+        nSprm = NS_ooxml::LN_Value_ST_Underline_dotted;
+        break;
+    case RTF_ULW:
+        nSprm = NS_ooxml::LN_Value_ST_Underline_words;
+        break;
+    default:
+        break;
+    }
+    if (nSprm >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nSprm);
+        m_aStates.top().aCharacterAttributes.set(NS_ooxml::LN_CT_Underline_val, pValue);
+        return RTFError::OK;
+    }
+
+    // Indentation
+    switch (nKeyword)
+    {
+    case RTF_QC:
+        nParam = NS_ooxml::LN_Value_ST_Jc_center;
+        break;
+    case RTF_QJ:
+        nParam = NS_ooxml::LN_Value_ST_Jc_both;
+        break;
+    case RTF_QL:
+        nParam = NS_ooxml::LN_Value_ST_Jc_left;
+        break;
+    case RTF_QR:
+        nParam = NS_ooxml::LN_Value_ST_Jc_right;
+        break;
+    case RTF_QD:
+        nParam = NS_ooxml::LN_Value_ST_Jc_both;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_jc, pValue);
+        m_bNeedPap = true;
+        return RTFError::OK;
+    }
+
+    // Font Alignment
+    switch (nKeyword)
+    {
+    case RTF_FAFIXED:
+    case RTF_FAAUTO:
+        nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_auto;
+        break;
+    case RTF_FAHANG:
+        nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_top;
+        break;
+    case RTF_FACENTER:
+        nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_center;
+        break;
+    case RTF_FAROMAN:
+        nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_baseline;
+        break;
+    case RTF_FAVAR:
+        nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_bottom;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_textAlignment, pValue);
+        return RTFError::OK;
+    }
+
+    // Tab kind.
+    switch (nKeyword)
+    {
+    case RTF_TQR:
+        nParam = NS_ooxml::LN_Value_ST_TabJc_right;
+        break;
+    case RTF_TQC:
+        nParam = NS_ooxml::LN_Value_ST_TabJc_center;
+        break;
+    case RTF_TQDEC:
+        nParam = NS_ooxml::LN_Value_ST_TabJc_decimal;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aTabAttributes.set(NS_ooxml::LN_CT_TabStop_val, pValue);
+        return RTFError::OK;
+    }
+
+    // Tab lead.
+    switch (nKeyword)
+    {
+    case RTF_TLDOT:
+        nParam = NS_ooxml::LN_Value_ST_TabTlc_dot;
+        break;
+    case RTF_TLMDOT:
+        nParam = NS_ooxml::LN_Value_ST_TabTlc_middleDot;
+        break;
+    case RTF_TLHYPH:
+        nParam = NS_ooxml::LN_Value_ST_TabTlc_hyphen;
+        break;
+    case RTF_TLUL:
+        nParam = NS_ooxml::LN_Value_ST_TabTlc_underscore;
+        break;
+    case RTF_TLTH:
+        nParam = NS_ooxml::LN_Value_ST_TabTlc_hyphen;
+        break; // thick line is not supported by dmapper, this is just a hack
+    case RTF_TLEQ:
+        nParam = NS_ooxml::LN_Value_ST_TabTlc_none;
+        break; // equal sign isn't, either
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aTabAttributes.set(NS_ooxml::LN_CT_TabStop_leader, pValue);
+        return RTFError::OK;
+    }
+
+    // Border types
+    {
+        switch (nKeyword)
+        {
+        // brdrhair and brdrs are the same, brdrw will make a difference
+        // map to values in ooxml/model.xml resource ST_Border
+        case RTF_BRDRHAIR:
+        case RTF_BRDRS:
+            nParam = NS_ooxml::LN_Value_ST_Border_single;
+            break;
+        case RTF_BRDRDOT:
+            nParam = NS_ooxml::LN_Value_ST_Border_dotted;
+            break;
+        case RTF_BRDRDASH:
+            nParam = NS_ooxml::LN_Value_ST_Border_dashed;
+            break;
+        case RTF_BRDRDB:
+            nParam = NS_ooxml::LN_Value_ST_Border_double;
+            break;
+        case RTF_BRDRTNTHSG:
+            nParam = NS_ooxml::LN_Value_ST_Border_thinThickSmallGap;
+            break;
+        case RTF_BRDRTNTHMG:
+            nParam = NS_ooxml::LN_Value_ST_Border_thinThickMediumGap;
+            break;
+        case RTF_BRDRTNTHLG:
+            nParam = NS_ooxml::LN_Value_ST_Border_thinThickLargeGap;
+            break;
+        case RTF_BRDRTHTNSG:
+            nParam = NS_ooxml::LN_Value_ST_Border_thickThinSmallGap;
+            break;
+        case RTF_BRDRTHTNMG:
+            nParam = NS_ooxml::LN_Value_ST_Border_thickThinMediumGap;
+            break;
+        case RTF_BRDRTHTNLG:
+            nParam = NS_ooxml::LN_Value_ST_Border_thickThinLargeGap;
+            break;
+        case RTF_BRDREMBOSS:
+            nParam = NS_ooxml::LN_Value_ST_Border_threeDEmboss;
+            break;
+        case RTF_BRDRENGRAVE:
+            nParam = NS_ooxml::LN_Value_ST_Border_threeDEngrave;
+            break;
+        case RTF_BRDROUTSET:
+            nParam = NS_ooxml::LN_Value_ST_Border_outset;
+            break;
+        case RTF_BRDRINSET:
+            nParam = NS_ooxml::LN_Value_ST_Border_inset;
+            break;
+        case RTF_BRDRNONE:
+            nParam = NS_ooxml::LN_Value_ST_Border_none;
+            break;
+        default:
+            break;
+        }
+        if (nParam >= 0)
+        {
+            auto pValue = std::make_shared<RTFValue>(nParam);
+            putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_val, pValue);
+            return RTFError::OK;
+        }
+    }
+
+    // Section breaks
+    switch (nKeyword)
+    {
+    case RTF_SBKNONE:
+        nParam = NS_ooxml::LN_Value_ST_SectionMark_continuous;
+        break;
+    case RTF_SBKCOL:
+        nParam = NS_ooxml::LN_Value_ST_SectionMark_nextColumn;
+        break;
+    case RTF_SBKPAGE:
+        nParam = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
+        break;
+    case RTF_SBKEVEN:
+        nParam = NS_ooxml::LN_Value_ST_SectionMark_evenPage;
+        break;
+    case RTF_SBKODD:
+        nParam = NS_ooxml::LN_Value_ST_SectionMark_oddPage;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        if (m_nResetBreakOnSectBreak != RTF_invalid)
+        {
+            m_nResetBreakOnSectBreak = nKeyword;
+        }
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aSectionSprms.set(NS_ooxml::LN_EG_SectPrContents_type, pValue);
+        return RTFError::OK;
+    }
+
+    // Footnote numbering
+    switch (nKeyword)
+    {
+    case RTF_FTNNAR:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_decimal;
+        break;
+    case RTF_FTNNALC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter;
+        break;
+    case RTF_FTNNAUC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperLetter;
+        break;
+    case RTF_FTNNRLC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman;
+        break;
+    case RTF_FTNNRUC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperRoman;
+        break;
+    case RTF_FTNNCHI:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_chicago;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        putNestedSprm(m_aDefaultState.aParagraphSprms, NS_ooxml::LN_EG_SectPrContents_footnotePr, NS_ooxml::LN_CT_FtnProps_numFmt, pValue);
+        return RTFError::OK;
+    }
+
+    // Footnote restart type
+    switch (nKeyword)
+    {
+    case RTF_FTNRSTPG:
+        nParam = NS_ooxml::LN_Value_ST_RestartNumber_eachPage;
+        break;
+    case RTF_FTNRESTART:
+        nParam = NS_ooxml::LN_Value_ST_RestartNumber_eachSect;
+        break;
+    case RTF_FTNRSTCONT:
+        nParam = NS_ooxml::LN_Value_ST_RestartNumber_continuous;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        putNestedSprm(m_aDefaultState.aParagraphSprms, NS_ooxml::LN_EG_SectPrContents_footnotePr, NS_ooxml::LN_EG_FtnEdnNumProps_numRestart, pValue);
+        return RTFError::OK;
+    }
+
+    // Endnote numbering
+    switch (nKeyword)
+    {
+    case RTF_AFTNNAR:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_decimal;
+        break;
+    case RTF_AFTNNALC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter;
+        break;
+    case RTF_AFTNNAUC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperLetter;
+        break;
+    case RTF_AFTNNRLC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman;
+        break;
+    case RTF_AFTNNRUC:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperRoman;
+        break;
+    case RTF_AFTNNCHI:
+        nParam = NS_ooxml::LN_Value_ST_NumberFormat_chicago;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        putNestedSprm(m_aDefaultState.aParagraphSprms, NS_ooxml::LN_EG_SectPrContents_endnotePr, NS_ooxml::LN_CT_EdnProps_numFmt, pValue);
+        return RTFError::OK;
+    }
+
+    switch (nKeyword)
+    {
+    case RTF_TRQL:
+        nParam = NS_ooxml::LN_Value_ST_Jc_left;
+        break;
+    case RTF_TRQC:
+        nParam = NS_ooxml::LN_Value_ST_Jc_center;
+        break;
+    case RTF_TRQR:
+        nParam = NS_ooxml::LN_Value_ST_Jc_right;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aTableRowSprms.set(NS_ooxml::LN_CT_TrPrBase_jc, pValue);
+        return RTFError::OK;
+    }
+
+    // Cell Text Flow
+    switch (nKeyword)
+    {
+    case RTF_CLTXLRTB:
+        nParam = NS_ooxml::LN_Value_ST_TextDirection_lrTb;
+        break;
+    case RTF_CLTXTBRL:
+        nParam = NS_ooxml::LN_Value_ST_TextDirection_tbRl;
+        break;
+    case RTF_CLTXBTLR:
+        nParam = NS_ooxml::LN_Value_ST_TextDirection_btLr;
+        break;
+    case RTF_CLTXLRTBV:
+        nParam = NS_ooxml::LN_Value_ST_TextDirection_lrTbV;
+        break;
+    case RTF_CLTXTBRLV:
+        nParam = NS_ooxml::LN_Value_ST_TextDirection_tbRlV;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aTableCellSprms.set(NS_ooxml::LN_CT_TcPrBase_textDirection, pValue);
+    }
+
+    // Trivial paragraph flags
+    switch (nKeyword)
+    {
+    case RTF_KEEP:
+        if (m_aStates.top().pCurrentBuffer != &m_aTableBufferStack.back())
+            nParam = NS_ooxml::LN_CT_PPrBase_keepLines;
+        break;
+    case RTF_KEEPN:
+        if (m_aStates.top().pCurrentBuffer != &m_aTableBufferStack.back())
+            nParam = NS_ooxml::LN_CT_PPrBase_keepNext;
+        break;
+    case RTF_INTBL:
+    {
+        m_aStates.top().pCurrentBuffer = &m_aTableBufferStack.back();
+        nParam = NS_ooxml::LN_inTbl;
+    }
+    break;
+    case RTF_PAGEBB:
+        nParam = NS_ooxml::LN_CT_PPrBase_pageBreakBefore;
+        break;
+    default:
+        break;
+    }
+    if (nParam >= 0)
+    {
+        auto pValue = std::make_shared<RTFValue>(1);
+        m_aStates.top().aParagraphSprms.erase(NS_ooxml::LN_inTbl);
+        m_aStates.top().aParagraphSprms.set(nParam, pValue);
+        return RTFError::OK;
+    }
+
+    switch (nKeyword)
+    {
+    case RTF_FNIL:
+    case RTF_FROMAN:
+    case RTF_FSWISS:
+    case RTF_FMODERN:
+    case RTF_FSCRIPT:
+    case RTF_FDECOR:
+    case RTF_FTECH:
+    case RTF_FBIDI:
+        // TODO ooxml:CT_Font_family seems to be ignored by the domain mapper
+        break;
+    case RTF_ANSI:
+        m_aStates.top().nCurrentEncoding = RTL_TEXTENCODING_MS_1252;
+        break;
+    case RTF_MAC:
+        m_aDefaultState.nCurrentEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
+        m_aStates.top().nCurrentEncoding = m_aDefaultState.nCurrentEncoding;
+        break;
+    case RTF_PC:
+        m_aDefaultState.nCurrentEncoding = RTL_TEXTENCODING_IBM_437;
+        m_aStates.top().nCurrentEncoding = m_aDefaultState.nCurrentEncoding;
+        break;
+    case RTF_PCA:
+        m_aDefaultState.nCurrentEncoding = RTL_TEXTENCODING_IBM_850;
+        m_aStates.top().nCurrentEncoding = m_aDefaultState.nCurrentEncoding;
+        break;
+    case RTF_PLAIN:
+    {
+        m_aStates.top().aCharacterSprms = getDefaultState().aCharacterSprms;
+        m_aStates.top().nCurrentEncoding = getEncoding(getFontIndex(m_nDefaultFontIndex));
+        m_aStates.top().aCharacterAttributes = getDefaultState().aCharacterAttributes;
+        m_aStates.top().nCurrentCharacterStyleIndex = -1;
+        m_aStates.top().isRightToLeft = false;
+        m_aStates.top().eRunType = RTFParserState::LOCH;
+    }
+    break;
+    case RTF_PARD:
+        if (m_bHadPicture)
+            dispatchSymbol(RTF_PAR);
+        // \pard is allowed between \cell and \row, but in that case it should not reset the fact that we're inside a table.
+        m_aStates.top().aParagraphSprms = m_aDefaultState.aParagraphSprms;
+        m_aStates.top().aParagraphAttributes = m_aDefaultState.aParagraphAttributes;
+
+        if (m_nTopLevelCells == 0 && m_nNestedCells == 0)
+        {
+            // Reset that we're in a table.
+            m_aStates.top().pCurrentBuffer = nullptr;
+        }
+        else
+        {
+            // We are still in a table.
+            m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_inTbl, std::make_shared<RTFValue>(1));
+            // Ideally getDefaultSPRM() would take care of this, but it would not when we're buffering.
+            m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_tabs, std::make_shared<RTFValue>());
+        }
+        resetFrame();
+
+        // Reset currently selected paragraph style as well.
+        // By default the style with index 0 is applied.
+        {
+            OUString const aName = getStyleName(0);
+            if (!aName.isEmpty())
+            {
+                m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_pStyle, std::make_shared<RTFValue>(aName));
+                m_aStates.top().nCurrentStyleIndex = 0;
+            }
+            else
+            {
+                m_aStates.top().nCurrentStyleIndex = -1;
+            }
+        }
+        // Need to send paragraph properties again, if there will be any.
+        m_bNeedPap = true;
+        break;
+    case RTF_SECTD:
+    {
+        m_aStates.top().aSectionSprms = m_aDefaultState.aSectionSprms;
+        m_aStates.top().aSectionAttributes = m_aDefaultState.aSectionAttributes;
+    }
+    break;
+    case RTF_TROWD:
+    {
+        // Back these up, in case later we still need this info.
+        backupTableRowProperties();
+        resetTableRowProperties();
+        // In case the table definition is in the middle of the row
+        // (invalid), make sure table definition is emitted.
+        m_bNeedPap = true;
+    }
+    break;
+    case RTF_WIDCTLPAR:
+    case RTF_NOWIDCTLPAR:
+    {
+        auto pValue = std::make_shared<RTFValue>(int(nKeyword == RTF_WIDCTLPAR));
+        m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_widowControl, pValue);
+    }
+    break;
+    case RTF_BOX:
+    {
+        RTFSprms aAttributes;
+        auto pValue = std::make_shared<RTFValue>(aAttributes);
+        for (int i = 0; i < 4; i++)
+            m_aStates.top().aParagraphSprms.set(getParagraphBorder(i), pValue);
+        m_aStates.top().nBorderState = RTFBorderState::PARAGRAPH_BOX;
+    }
+    break;
+    case RTF_LTRSECT:
+    case RTF_RTLSECT:
+    {
+        auto pValue = std::make_shared<RTFValue>(nKeyword == RTF_LTRSECT ? 0 : 1);
+        m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_EG_SectPrContents_textDirection, pValue);
+    }
+    break;
+    case RTF_LTRPAR:
+    case RTF_RTLPAR:
+    {
+        auto pValue = std::make_shared<RTFValue>(nKeyword == RTF_LTRPAR ? 0 : 1);
+        m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_bidi, pValue);
+    }
+    break;
+    case RTF_LTRROW:
+    case RTF_RTLROW:
+        m_aStates.top().aTableRowSprms.set(NS_ooxml::LN_CT_TblPrBase_bidiVisual, std::make_shared<RTFValue>(int(nKeyword == RTF_RTLROW)));
+        break;
+    case RTF_LTRCH:
+        // dmapper does not support this.
+        m_aStates.top().isRightToLeft = false;
+        break;
+    case RTF_RTLCH:
+        m_aStates.top().isRightToLeft = true;
+        if (m_aDefaultState.nCurrentEncoding == RTL_TEXTENCODING_MS_1255)
+            m_aStates.top().nCurrentEncoding = m_aDefaultState.nCurrentEncoding;
+        break;
+    case RTF_ULNONE:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_Underline_none);
+        m_aStates.top().aCharacterAttributes.set(NS_ooxml::LN_CT_Underline_val, pValue);
+    }
+    break;
+    case RTF_NONSHPPICT:
+    case RTF_MMATHPICT: // Picture group used by readers not understanding \moMath group
+        m_aStates.top().eDestination = Destination::SKIP;
+        break;
+    case RTF_CLBRDRT:
+    case RTF_CLBRDRL:
+    case RTF_CLBRDRB:
+    case RTF_CLBRDRR:
+    {
+        RTFSprms aAttributes;
+        RTFSprms aSprms;
+        auto pValue = std::make_shared<RTFValue>(aAttributes, aSprms);
+        switch (nKeyword)
+        {
+        case RTF_CLBRDRT:
+            nParam = NS_ooxml::LN_CT_TcBorders_top;
+            break;
+        case RTF_CLBRDRL:
+            nParam = NS_ooxml::LN_CT_TcBorders_left;
+            break;
+        case RTF_CLBRDRB:
+            nParam = NS_ooxml::LN_CT_TcBorders_bottom;
+            break;
+        case RTF_CLBRDRR:
+            nParam = NS_ooxml::LN_CT_TcBorders_right;
+            break;
+        default:
+            break;
+        }
+        putNestedSprm(m_aStates.top().aTableCellSprms, NS_ooxml::LN_CT_TcPrBase_tcBorders, nParam, pValue);
+        m_aStates.top().nBorderState = RTFBorderState::CELL;
+    }
+    break;
+    case RTF_PGBRDRT:
+    case RTF_PGBRDRL:
+    case RTF_PGBRDRB:
+    case RTF_PGBRDRR:
+    {
+        RTFSprms aAttributes;
+        RTFSprms aSprms;
+        auto pValue = std::make_shared<RTFValue>(aAttributes, aSprms);
+        switch (nKeyword)
+        {
+        case RTF_PGBRDRT:
+            nParam = NS_ooxml::LN_CT_PageBorders_top;
+            break;
+        case RTF_PGBRDRL:
+            nParam = NS_ooxml::LN_CT_PageBorders_left;
+            break;
+        case RTF_PGBRDRB:
+            nParam = NS_ooxml::LN_CT_PageBorders_bottom;
+            break;
+        case RTF_PGBRDRR:
+            nParam = NS_ooxml::LN_CT_PageBorders_right;
+            break;
+        default:
+            break;
+        }
+        putNestedSprm(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgBorders, nParam, pValue);
+        m_aStates.top().nBorderState = RTFBorderState::PAGE;
+    }
+    break;
+    case RTF_BRDRT:
+    case RTF_BRDRL:
+    case RTF_BRDRB:
+    case RTF_BRDRR:
+    {
+        RTFSprms aAttributes;
+        RTFSprms aSprms;
+        auto pValue = std::make_shared<RTFValue>(aAttributes, aSprms);
+        switch (nKeyword)
+        {
+        case RTF_BRDRT:
+            nParam = getParagraphBorder(0);
+            break;
+        case RTF_BRDRL:
+            nParam = getParagraphBorder(1);
+            break;
+        case RTF_BRDRB:
+            nParam = getParagraphBorder(2);
+            break;
+        case RTF_BRDRR:
+            nParam = getParagraphBorder(3);
+            break;
+        default:
+            break;
+        }
+        putNestedSprm(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PrBase_pBdr, nParam, pValue);
+        m_aStates.top().nBorderState = RTFBorderState::PARAGRAPH;
+    }
+    break;
+    case RTF_CHBRDR:
+    {
+        RTFSprms aAttributes;
+        auto pValue = std::make_shared<RTFValue>(aAttributes);
+        m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_bdr, pValue);
+        m_aStates.top().nBorderState = RTFBorderState::CHARACTER;
+    }
+    break;
+    case RTF_CLMGF:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_Merge_restart);
+        m_aStates.top().aTableCellSprms.set(NS_ooxml::LN_CT_TcPrBase_hMerge, pValue);
+    }
+    break;
+    case RTF_CLMRG:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_Merge_continue);
+        m_aStates.top().aTableCellSprms.set(NS_ooxml::LN_CT_TcPrBase_hMerge, pValue);
+    }
+    break;
+    case RTF_CLVMGF:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_Merge_restart);
+        m_aStates.top().aTableCellSprms.set(NS_ooxml::LN_CT_TcPrBase_vMerge, pValue);
+    }
+    break;
+    case RTF_CLVMRG:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_Merge_continue);
+        m_aStates.top().aTableCellSprms.set(NS_ooxml::LN_CT_TcPrBase_vMerge, pValue);
+    }
+    break;
+    case RTF_CLVERTALT:
+    case RTF_CLVERTALC:
+    case RTF_CLVERTALB:
+    {
+        switch (nKeyword)
+        {
+        case RTF_CLVERTALT:
+            nParam = NS_ooxml::LN_Value_ST_VerticalJc_top;
+            break;
+        case RTF_CLVERTALC:
+            nParam = NS_ooxml::LN_Value_ST_VerticalJc_center;
+            break;
+        case RTF_CLVERTALB:
+            nParam = NS_ooxml::LN_Value_ST_VerticalJc_bottom;
+            break;
+        default:
+            break;
+        }
+        auto pValue = std::make_shared<RTFValue>(nParam);
+        m_aStates.top().aTableCellSprms.set(NS_ooxml::LN_CT_TcPrBase_vAlign, pValue);
+    }
+    break;
+    case RTF_TRKEEP:
+    {
+        auto pValue = std::make_shared<RTFValue>(1);
+        m_aStates.top().aTableRowSprms.set(NS_ooxml::LN_CT_TrPrBase_cantSplit, pValue);
+    }
+    break;
+    case RTF_SECTUNLOCKED:
+    {
+        auto pValue = std::make_shared<RTFValue>(int(!nParam));
+        m_aStates.top().aSectionSprms.set(NS_ooxml::LN_EG_SectPrContents_formProt, pValue);
+    }
+    break;
+    case RTF_PGNBIDIA:
+    case RTF_PGNBIDIB:
+        // These should be mapped to NS_ooxml::LN_EG_SectPrContents_pgNumType, but dmapper has no API for that at the moment.
+        break;
+    case RTF_LOCH:
+        m_aStates.top().eRunType = RTFParserState::LOCH;
+        break;
+    case RTF_HICH:
+        m_aStates.top().eRunType = RTFParserState::HICH;
+        break;
+    case RTF_DBCH:
+        m_aStates.top().eRunType = RTFParserState::DBCH;
+        break;
+    case RTF_TITLEPG:
+    {
+        auto pValue = std::make_shared<RTFValue>(1);
+        m_aStates.top().aSectionSprms.set(NS_ooxml::LN_EG_SectPrContents_titlePg, pValue);
+    }
+    break;
+    case RTF_SUPER:
+    {
+        if (!m_aStates.top().pCurrentBuffer)
+            m_aStates.top().pCurrentBuffer = &m_aSuperBuffer;
+
+        auto pValue = std::make_shared<RTFValue>("superscript");
+        m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_vertAlign, pValue);
+    }
+    break;
+    case RTF_SUB:
+    {
+        auto pValue = std::make_shared<RTFValue>("subscript");
+        m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_vertAlign, pValue);
+    }
+    break;
+    case RTF_NOSUPERSUB:
+    {
+        if (m_aStates.top().pCurrentBuffer == &m_aSuperBuffer)
+        {
+            replayBuffer(m_aSuperBuffer, nullptr, nullptr);
+            m_aStates.top().pCurrentBuffer = nullptr;
+        }
+        m_aStates.top().aCharacterSprms.erase(NS_ooxml::LN_EG_RPrBase_vertAlign);
+    }
+    break;
+    case RTF_LINEPPAGE:
+    case RTF_LINECONT:
+    {
+        auto pValue = std::make_shared<RTFValue>(nKeyword == RTF_LINEPPAGE ? NS_ooxml::LN_Value_ST_LineNumberRestart_newPage : NS_ooxml::LN_Value_ST_LineNumberRestart_continuous);
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_lnNumType, NS_ooxml::LN_CT_LineNumber_restart, pValue);
+    }
+    break;
+    case RTF_AENDDOC:
+        // Noop, this is the default in Writer.
+        break;
+    case RTF_AENDNOTES:
+        // Noop, Writer does not support having endnotes at the end of section.
+        break;
+    case RTF_AFTNRSTCONT:
+        // Noop, this is the default in Writer.
+        break;
+    case RTF_AFTNRESTART:
+        // Noop, Writer does not support restarting endnotes at each section.
+        break;
+    case RTF_FTNBJ:
+        // Noop, this is the default in Writer.
+        break;
+    case RTF_ENDDOC:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_RestartNumber_eachSect);
+        putNestedSprm(m_aDefaultState.aParagraphSprms, NS_ooxml::LN_EG_SectPrContents_footnotePr, NS_ooxml::LN_EG_FtnEdnNumProps_numRestart, pValue);
+    }
+    break;
+    case RTF_NOLINE:
+        eraseNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_lnNumType, NS_ooxml::LN_CT_LineNumber_distance);
+        break;
+    case RTF_FORMSHADE:
+        // Noop, this is the default in Writer.
+        break;
+    case RTF_PNGBLIP:
+        m_aStates.top().aPicture.eStyle = RTFBmpStyle::PNG;
+        break;
+    case RTF_JPEGBLIP:
+        m_aStates.top().aPicture.eStyle = RTFBmpStyle::JPEG;
+        break;
+    case RTF_POSYT:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, NS_ooxml::LN_Value_doc_ST_YAlign_top);
+        break;
+    case RTF_POSYB:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, NS_ooxml::LN_Value_doc_ST_YAlign_bottom);
+        break;
+    case RTF_POSYC:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, NS_ooxml::LN_Value_doc_ST_YAlign_center);
+        break;
+    case RTF_POSYIN:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, NS_ooxml::LN_Value_doc_ST_YAlign_inside);
+        break;
+    case RTF_POSYOUT:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, NS_ooxml::LN_Value_doc_ST_YAlign_outside);
+        break;
+    case RTF_POSYIL:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, NS_ooxml::LN_Value_doc_ST_YAlign_inline);
+        break;
+
+    case RTF_PHMRG:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_Value_doc_ST_HAnchor_margin);
+        break;
+    case RTF_PVMRG:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, NS_ooxml::LN_Value_doc_ST_VAnchor_margin);
+        break;
+    case RTF_PHPG:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_Value_doc_ST_HAnchor_page);
+        break;
+    case RTF_PVPG:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, NS_ooxml::LN_Value_doc_ST_VAnchor_page);
+        break;
+    case RTF_PHCOL:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_Value_doc_ST_HAnchor_text);
+        break;
+    case RTF_PVPARA:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, NS_ooxml::LN_Value_doc_ST_VAnchor_text);
+        break;
+
+    case RTF_POSXC:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_Value_doc_ST_XAlign_center);
+        break;
+    case RTF_POSXI:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_Value_doc_ST_XAlign_inside);
+        break;
+    case RTF_POSXO:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_Value_doc_ST_XAlign_outside);
+        break;
+    case RTF_POSXL:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_Value_doc_ST_XAlign_left);
+        break;
+    case RTF_POSXR:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_Value_doc_ST_XAlign_right);
+        break;
+
+    case RTF_DPLINE:
+    case RTF_DPRECT:
+    case RTF_DPELLIPSE:
+    case RTF_DPTXBX:
+    case RTF_DPPOLYLINE:
+    case RTF_DPPOLYGON:
+    {
+        sal_Int32 nType = 0;
+        switch (nKeyword)
+        {
+        case RTF_DPLINE:
+            m_aStates.top().aDrawingObject.xShape.set(getModelFactory()->createInstance("com.sun.star.drawing.LineShape"), uno::UNO_QUERY);
+            break;
+        case RTF_DPPOLYLINE:
+            // The reason this is not a simple CustomShape is that in the old syntax we have no ViewBox info.
+            m_aStates.top().aDrawingObject.xShape.set(getModelFactory()->createInstance("com.sun.star.drawing.PolyLineShape"), uno::UNO_QUERY);
+            break;
+        case RTF_DPPOLYGON:
+            m_aStates.top().aDrawingObject.xShape.set(getModelFactory()->createInstance("com.sun.star.drawing.PolyPolygonShape"), uno::UNO_QUERY);
+            break;
+        case RTF_DPRECT:
+            m_aStates.top().aDrawingObject.xShape.set(getModelFactory()->createInstance("com.sun.star.drawing.RectangleShape"), uno::UNO_QUERY);
+            break;
+        case RTF_DPELLIPSE:
+            nType = ESCHER_ShpInst_Ellipse;
+            break;
+        case RTF_DPTXBX:
+        {
+            m_aStates.top().aDrawingObject.xShape.set(getModelFactory()->createInstance("com.sun.star.text.TextFrame"), uno::UNO_QUERY);
+            std::vector<beans::PropertyValue> aDefaults = RTFSdrImport::getTextFrameDefaults(false);
+            for (std::size_t i = 0; i < aDefaults.size(); ++i)
+            {
+                if (!findPropertyName(m_aStates.top().aDrawingObject.aPendingProperties, aDefaults[i].Name))
+                    m_aStates.top().aDrawingObject.aPendingProperties.push_back(aDefaults[i]);
+            }
+            checkFirstRun();
+            Mapper().startShape(m_aStates.top().aDrawingObject.xShape);
+            m_aStates.top().aDrawingObject.bHadShapeText = true;
+        }
+        break;
+        default:
+            break;
+        }
+        if (nType)
+            m_aStates.top().aDrawingObject.xShape.set(getModelFactory()->createInstance("com.sun.star.drawing.CustomShape"), uno::UNO_QUERY);
+        uno::Reference<drawing::XDrawPageSupplier> xDrawSupplier(m_xDstDoc, uno::UNO_QUERY);
+        if (xDrawSupplier.is())
+        {
+            uno::Reference<drawing::XShapes> xShapes(xDrawSupplier->getDrawPage(), uno::UNO_QUERY);
+            if (xShapes.is() && nKeyword != RTF_DPTXBX)
+                xShapes->add(m_aStates.top().aDrawingObject.xShape);
+        }
+        if (nType)
+        {
+            uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(m_aStates.top().aDrawingObject.xShape, uno::UNO_QUERY);
+            xDefaulter->createCustomShapeDefaults(OUString::number(nType));
+        }
+        m_aStates.top().aDrawingObject.xPropertySet.set(m_aStates.top().aDrawingObject.xShape, uno::UNO_QUERY);
+        std::vector<beans::PropertyValue>& rPendingProperties = m_aStates.top().aDrawingObject.aPendingProperties;
+        for (std::vector<beans::PropertyValue>::iterator i = rPendingProperties.begin(); i != rPendingProperties.end(); ++i)
+            m_aStates.top().aDrawingObject.xPropertySet->setPropertyValue(i->Name, i->Value);
+        m_pSdrImport->resolveDhgt(m_aStates.top().aDrawingObject.xPropertySet, m_aStates.top().aDrawingObject.nDhgt, /*bOldStyle=*/true);
+    }
+    break;
+    case RTF_DOBXMARGIN:
+    case RTF_DOBYMARGIN:
+    {
+        beans::PropertyValue aPropertyValue;
+        aPropertyValue.Name = (nKeyword == RTF_DOBXMARGIN ? OUString("HoriOrientRelation") : OUString("VertOrientRelation"));
+        aPropertyValue.Value <<= text::RelOrientation::PAGE_PRINT_AREA;
+        m_aStates.top().aDrawingObject.aPendingProperties.push_back(aPropertyValue);
+    }
+    break;
+    case RTF_DOBXPAGE:
+    case RTF_DOBYPAGE:
+    {
+        beans::PropertyValue aPropertyValue;
+        aPropertyValue.Name = (nKeyword == RTF_DOBXPAGE ? OUString("HoriOrientRelation") : OUString("VertOrientRelation"));
+        aPropertyValue.Value <<= text::RelOrientation::PAGE_FRAME;
+        m_aStates.top().aDrawingObject.aPendingProperties.push_back(aPropertyValue);
+    }
+    break;
+    case RTF_DOBYPARA:
+    {
+        beans::PropertyValue aPropertyValue;
+        aPropertyValue.Name = "VertOrientRelation";
+        aPropertyValue.Value <<= text::RelOrientation::FRAME;
+        m_aStates.top().aDrawingObject.aPendingProperties.push_back(aPropertyValue);
+    }
+    break;
+    case RTF_CONTEXTUALSPACE:
+    {
+        auto pValue = std::make_shared<RTFValue>(1);
+        m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_contextualSpacing, pValue);
+    }
+    break;
+    case RTF_LINKSTYLES:
+    {
+        auto pValue = std::make_shared<RTFValue>(1);
+        m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_linkStyles, pValue);
+    }
+    break;
+    case RTF_PNLVLBODY:
+    {
+        auto pValue = std::make_shared<RTFValue>(2);
+        m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, pValue);
+    }
+    break;
+    case RTF_PNDEC:
+    {
+        auto pValue = std::make_shared<RTFValue>(0); // decimal, same as \levelnfc0
+        m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, pValue);
+    }
+    break;
+    case RTF_PNLVLBLT:
+    {
+        m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, std::make_shared<RTFValue>(1));
+        m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, std::make_shared<RTFValue>(23)); // bullets, same as \levelnfc23
+    }
+    break;
+    case RTF_LANDSCAPE: // fall through: set the default + current value
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_PageOrientation_landscape);
+        putNestedAttribute(m_aDefaultState.aSectionSprms,
+                           NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_orient, pValue);
+    }
+    case RTF_LNDSCPSXN:
+    {
+        auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_PageOrientation_landscape);
+        putNestedAttribute(m_aStates.top().aSectionSprms,
+                           NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_orient, pValue);
+    }
+    break;
+    case RTF_SHPBXPAGE:
+        m_aStates.top().aShape.nHoriOrientRelation = text::RelOrientation::PAGE_FRAME;
+        m_aStates.top().aShape.nHoriOrientRelationToken = NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_page;
+        break;
+    case RTF_SHPBYPAGE:
+        m_aStates.top().aShape.nVertOrientRelation = text::RelOrientation::PAGE_FRAME;
+        m_aStates.top().aShape.nVertOrientRelationToken = NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_page;
+        break;
+    case RTF_DPLINEHOLLOW:
+        m_aStates.top().aDrawingObject.nFLine = 0;
+        break;
+    case RTF_DPROUNDR:
+        if (m_aStates.top().aDrawingObject.xPropertySet.is())
+            // Seems this old syntax has no way to specify a custom radius, and this is the default
+            m_aStates.top().aDrawingObject.xPropertySet->setPropertyValue("CornerRadius", uno::makeAny(sal_Int32(83)));
+        break;
+    case RTF_NOWRAP:
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_wrap, NS_ooxml::LN_Value_doc_ST_Wrap_notBeside);
+        break;
+    case RTF_MNOR:
+        m_bMathNor = true;
+        break;
+    case RTF_REVISIONS:
+        m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_trackRevisions, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_BRDRSH:
+        putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_shadow, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_NOCOLBAL:
+        m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Compat_noColumnBalance, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_MARGMIRROR:
+        m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_mirrorMargins, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_SAUTOUPD:
+        m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Style_autoRedefine, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_WIDOWCTRL:
+        m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_widowControl, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_LINEBETCOL:
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_cols, NS_ooxml::LN_CT_Columns_sep, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_PGNRESTART:
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgNumType, NS_ooxml::LN_CT_PageNumber_start, std::make_shared<RTFValue>(1));
+        break;
+    case RTF_PGNUCLTR:
+    {
+        auto pIntValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_NumberFormat_upperLetter);
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgNumType, NS_ooxml::LN_CT_PageNumber_fmt, pIntValue);
+    }
+    break;
+    case RTF_PGNLCLTR:
+    {
+        auto pIntValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter);
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgNumType, NS_ooxml::LN_CT_PageNumber_fmt, pIntValue);
+    }
+    break;
+    case RTF_PGNUCRM:
+    {
+        auto pIntValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_NumberFormat_upperRoman);
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgNumType, NS_ooxml::LN_CT_PageNumber_fmt, pIntValue);
+    }
+    break;
+    case RTF_PGNLCRM:
+    {
+        auto pIntValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman);
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgNumType, NS_ooxml::LN_CT_PageNumber_fmt, pIntValue);
+    }
+    break;
+    case RTF_PGNDEC:
+    {
+        auto pIntValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_NumberFormat_decimal);
+        putNestedAttribute(m_aStates.top().aSectionSprms, NS_ooxml::LN_EG_SectPrContents_pgNumType, NS_ooxml::LN_CT_PageNumber_fmt, pIntValue);
+    }
+    break;
+    default:
+    {
+        SAL_INFO("writerfilter", "TODO handle flag '" << keywordToString(nKeyword) << "'");
+        aSkip.setParsed(false);
+    }
+    break;
+    }
+    return RTFError::OK;
+}
+
+} // namespace rtftok
+} // namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
new file mode 100644
index 0000000..98aa43d
--- /dev/null
+++ b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
@@ -0,0 +1,409 @@
+/* -*- 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 <svl/lngmisc.hxx>
+
+#include <ooxml/resourceids.hxx>
+
+#include <rtfreferenceproperties.hxx>
+#include <rtfskipdestination.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter
+{
+namespace rtftok
+{
+
+RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
+{
+    setNeedSect(true);
+    if (nKeyword != RTF_HEXCHAR)
+        checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true);
+    else
+        checkUnicode(/*bUnicode =*/ true, /*bHex =*/ false);
+    RTFSkipDestination aSkip(*this);
+
+    if (RTF_LINE == nKeyword)
+    {
+        // very special handling since text() will eat lone '\n'
+        singleChar('\n');
+        return RTFError::OK;
+    }
+    // Trivial symbols
+    sal_uInt8 cCh = 0;
+    switch (nKeyword)
+    {
+    case RTF_TAB:
+        cCh = '\t';
+        break;
+    case RTF_BACKSLASH:
+        cCh = '\\';
+        break;
+    case RTF_LBRACE:
+        cCh = '{';
+        break;
+    case RTF_RBRACE:
+        cCh = '}';
+        break;
+    case RTF_EMDASH:
+        cCh = 151;
+        break;
+    case RTF_ENDASH:
+        cCh = 150;
+        break;
+    case RTF_BULLET:
+        cCh = 149;
+        break;
+    case RTF_LQUOTE:
+        cCh = 145;
+        break;
+    case RTF_RQUOTE:
+        cCh = 146;
+        break;
+    case RTF_LDBLQUOTE:
+        cCh = 147;
+        break;
+    case RTF_RDBLQUOTE:
+        cCh = 148;
+        break;
+    default:
+        break;
+    }
+    if (cCh > 0)
+    {
+        OUString aStr(OStringToOUString(OString(cCh), RTL_TEXTENCODING_MS_1252));
+        text(aStr);
+        return RTFError::OK;
+    }
+
+    switch (nKeyword)
+    {
+    case RTF_IGNORE:
+    {
+        m_bSkipUnknown = true;
+        aSkip.setReset(false);
+        return RTFError::OK;
+    }
+    break;
+    case RTF_PAR:
+    {
+        if (m_aStates.top().eDestination == Destination::FOOTNOTESEPARATOR)
+            break; // just ignore it - only thing we read in here is CHFTNSEP
+        checkFirstRun();
+        bool bNeedPap = m_bNeedPap;
+        checkNeedPap();
+        if (bNeedPap)
+            runProps();
+        if (!m_aStates.top().pCurrentBuffer)
+        {
+            parBreak();
+            // Not in table? Reset max width.
+            if (m_nCellxMax)
+            {
+                // Was in table, but not anymore -> tblEnd.
+                RTFSprms aAttributes;
+                RTFSprms aSprms;
+                aSprms.set(NS_ooxml::LN_tblEnd, std::make_shared<RTFValue>(1));
+                writerfilter::Reference<Properties>::Pointer_t pProperties = std::make_shared<RTFReferenceProperties>(aAttributes, aSprms);
+                Mapper().props(pProperties);
+            }
+            m_nCellxMax = 0;
+        }
+        else if (m_aStates.top().eDestination != Destination::SHAPETEXT)
+        {
+            RTFValue::Pointer_t pValue;
+            m_aStates.top().pCurrentBuffer->push_back(
+                Buf_t(BUFFER_PAR, pValue, nullptr));
+        }
+        // but don't emit properties yet, since they may change till the first text token arrives
+        m_bNeedPap = true;
+        if (!m_aStates.top().aFrame.inFrame())
+            m_bNeedPar = false;
+        m_bNeedFinalPar = false;
+    }
+    break;
+    case RTF_SECT:
+    {
+        m_bHadSect = true;
+        if (m_bIgnoreNextContSectBreak)
+            m_bIgnoreNextContSectBreak = false;
+        else
+        {
+            sectBreak();
+            if (m_nResetBreakOnSectBreak != RTF_invalid)
+            {
+                // this should run on _second_ \sect after \page
+                dispatchSymbol(m_nResetBreakOnSectBreak); // lazy reset
+                m_nResetBreakOnSectBreak = RTF_invalid;
+                m_bNeedSect = false; // dispatchSymbol set it
+            }
+        }
+    }
+    break;
+    case RTF_NOBREAK:
+    {
+        OUString aStr(SVT_HARD_SPACE);
+        text(aStr);
+    }
+    break;
+    case RTF_NOBRKHYPH:
+    {
+        OUString aStr(SVT_HARD_HYPHEN);
+        text(aStr);
+    }
+    break;
+    case RTF_OPTHYPH:
+    {
+        OUString aStr(SVT_SOFT_HYPHEN);
+        text(aStr);
+    }
+    break;
+    case RTF_HEXCHAR:
+        m_aStates.top().nInternalState = RTFInternalState::HEX;
+        break;
+    case RTF_CELL:
+    case RTF_NESTCELL:
+    {
+        checkFirstRun();
+        if (m_bNeedPap)
+        {
+            // There were no runs in the cell, so we need to send paragraph and character properties here.
+            auto pPValue = std::make_shared<RTFValue>(m_aStates.top().aParagraphAttributes, m_aStates.top().aParagraphSprms);
+            m_aTableBufferStack.back().push_back(Buf_t(BUFFER_PROPS, pPValue, nullptr));
+            auto pCValue = std::make_shared<RTFValue>(m_aStates.top().aCharacterAttributes, m_aStates.top().aCharacterSprms);
+            m_aTableBufferStack.back().push_back(Buf_t(BUFFER_PROPS, pCValue, nullptr));
+        }
+
+        RTFValue::Pointer_t pValue;
+        m_aTableBufferStack.back().push_back(Buf_t(BUFFER_CELLEND, pValue, nullptr));
+        m_bNeedPap = true;
+    }
+    break;
+    case RTF_NESTROW:
+    {
+        std::shared_ptr<TableRowBuffer> const pBuffer(
+            new TableRowBuffer(
+                m_aTableBufferStack.back(),
+                m_aNestedTableCellsSprms,
+                m_aNestedTableCellsAttributes,
+                m_nNestedCells));
+        prepareProperties(m_aStates.top(),
+                          pBuffer->pParaProperties,
+                          pBuffer->pFrameProperties,
+                          pBuffer->pRowProperties,
+                          m_nNestedCells, m_nNestedCurrentCellX);
+
+        assert(m_aStates.top().pCurrentBuffer == &m_aTableBufferStack.back());
+        if (m_aTableBufferStack.size() == 1)
+        {
+            throw io::WrongFormatException(
+                "mismatch between \\itap and number of \\nestrow", nullptr);
+        }
+        // note: there may be several states pointing to table buffer!
+        for (std::size_t i = 0; i < m_aStates.size(); ++i)
+        {
+            if (m_aStates[i].pCurrentBuffer == &m_aTableBufferStack.back())
+            {
+                m_aStates[i].pCurrentBuffer =
+                    &m_aTableBufferStack[m_aTableBufferStack.size()-2];
+            }
+        }
+        m_aTableBufferStack.pop_back();
+        m_aTableBufferStack.back().push_back(
+            Buf_t(BUFFER_NESTROW, RTFValue::Pointer_t(), pBuffer));
+
+        m_aNestedTableCellsSprms.clear();
+        m_aNestedTableCellsAttributes.clear();
+        m_nNestedCells = 0;
+        m_bNeedPap = true;
+    }
+    break;
+    case RTF_ROW:
+    {
+        bool bRestored = false;
+        // Ending a row, but no cells defined?
+        // See if there was an invalid table row reset, so we can restore cell infos to help invalid documents.
+        if (!m_nTopLevelCurrentCellX && m_nBackupTopLevelCurrentCellX)
+        {
+            restoreTableRowProperties();
+            bRestored = true;
+        }
+
+        // If the right edge of the last cell (row width) is smaller than the width of some other row, mimic WW8TabDesc::CalcDefaults(): resize the last cell
+        const int MINLAY = 23; // sw/inc/swtypes.hxx, minimal possible size of frames.
+        if ((m_nCellxMax - m_nTopLevelCurrentCellX) >= MINLAY)
+        {
+            auto pXValueLast = m_aStates.top().aTableRowSprms.find(NS_ooxml::LN_CT_TblGridBase_gridCol, false);
+            auto pXValue = std::make_shared<RTFValue>(pXValueLast->getInt() + m_nCellxMax - m_nTopLevelCurrentCellX);
+            m_aStates.top().aTableRowSprms.eraseLast(NS_ooxml::LN_CT_TblGridBase_gridCol);
+            m_aStates.top().aTableRowSprms.set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue, RTFOverwrite::NO_APPEND);
+            m_nTopLevelCurrentCellX = m_nCellxMax;
+        }
+
+        if (m_nTopLevelCells)
+        {
+            // Make a backup before we start popping elements
+            m_aTableInheritingCellsSprms = m_aTopLevelTableCellsSprms;
+            m_aTableInheritingCellsAttributes = m_aTopLevelTableCellsAttributes;
+            m_nInheritingCells = m_nTopLevelCells;
+        }
+        else
+        {
+            // No table definition? Then inherit from the previous row
+            m_aTopLevelTableCellsSprms = m_aTableInheritingCellsSprms;
+            m_aTopLevelTableCellsAttributes = m_aTableInheritingCellsAttributes;
+            m_nTopLevelCells = m_nInheritingCells;
+        }
+
+        while (m_aTableBufferStack.size() > 1)
+        {
+            SAL_WARN("writerfilter.rtf", "dropping extra table buffer");
+            // note: there may be several states pointing to table buffer!
+            for (std::size_t i = 0; i < m_aStates.size(); ++i)
+            {
+                if (m_aStates[i].pCurrentBuffer == &m_aTableBufferStack.back())
+                {
+                    m_aStates[i].pCurrentBuffer =
+                        &m_aTableBufferStack.front();
+                }
+            }
+            m_aTableBufferStack.pop_back();
+        }
+
+        replayRowBuffer(m_aTableBufferStack.back(),
+                        m_aTopLevelTableCellsSprms, m_aTopLevelTableCellsAttributes,
+                        m_nTopLevelCells);
+
+        m_aStates.top().aTableCellSprms = m_aDefaultState.aTableCellSprms;
+        m_aStates.top().aTableCellAttributes = m_aDefaultState.aTableCellAttributes;
+
+        writerfilter::Reference<Properties>::Pointer_t paraProperties;
+        writerfilter::Reference<Properties>::Pointer_t frameProperties;
+        writerfilter::Reference<Properties>::Pointer_t rowProperties;
+        prepareProperties(m_aStates.top(),
+                          paraProperties, frameProperties, rowProperties,
+                          m_nTopLevelCells, m_nTopLevelCurrentCellX);
+        sendProperties(paraProperties, frameProperties, rowProperties);
+
+        m_bNeedPap = true;
+        m_bNeedFinalPar = true;
+        m_aTableBufferStack.back().clear();
+        m_nTopLevelCells = 0;
+
+        if (bRestored)
+            // We restored cell definitions, clear these now.
+            // This is necessary, as later cell definitions want to overwrite the restored ones.
+            resetTableRowProperties();
+    }
+    break;
+    case RTF_COLUMN:
+    {
+        bool bColumns = false; // If we have multiple columns
+        RTFValue::Pointer_t pCols = m_aStates.top().aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_cols);
+        if (pCols.get())
+        {
+            RTFValue::Pointer_t pNum = pCols->getAttributes().find(NS_ooxml::LN_CT_Columns_num);
+            if (pNum.get() && pNum->getInt() > 1)
+                bColumns = true;
+        }
+        checkFirstRun();
+        if (bColumns)
+        {
+            sal_uInt8 sBreak[] = { 0xe };
+            Mapper().startCharacterGroup();
+            Mapper().text(sBreak, 1);
+            Mapper().endCharacterGroup();
+        }
+        else
+            dispatchSymbol(RTF_PAGE);
+    }
+    break;
+    case RTF_CHFTN:
+    {
+        if (m_aStates.top().pCurrentBuffer == &m_aSuperBuffer)
+            // Stop buffering, there will be no custom mark for this footnote or endnote.
+            m_aStates.top().pCurrentBuffer = nullptr;
+        break;
+    }
+    case RTF_PAGE:
+    {
+        // Ignore page breaks inside tables.
+        if (m_aStates.top().pCurrentBuffer == &m_aTableBufferStack.back())
+            break;
+
+        // If we're inside a continuous section, we should send a section break, not a page one.
+        RTFValue::Pointer_t pBreak = m_aStates.top().aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_type);
+        // Unless we're on a title page.
+        RTFValue::Pointer_t pTitlePg = m_aStates.top().aSectionSprms.find(NS_ooxml::LN_EG_SectPrContents_titlePg);
+        if (((pBreak.get() && pBreak->getInt() == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous))
+                || m_nResetBreakOnSectBreak == RTF_SBKNONE)
+                && !(pTitlePg.get() && pTitlePg->getInt()))
+        {
+            if (m_bWasInFrame)
+            {
+                dispatchSymbol(RTF_PAR);
+                m_bWasInFrame = false;
+            }
+            sectBreak();
+            // note: this will not affect the following section break
+            // but the one just pushed
+            dispatchFlag(RTF_SBKPAGE);
+            if (m_bNeedPar)
+                dispatchSymbol(RTF_PAR);
+            m_bIgnoreNextContSectBreak = true;
+            // arrange to clean up the synthetic RTF_SBKPAGE
+            m_nResetBreakOnSectBreak = RTF_SBKNONE;
+        }
+        else
+        {
+            checkFirstRun();
+            checkNeedPap();
+            sal_uInt8 sBreak[] = { 0xc };
+            Mapper().text(sBreak, 1);
+            if (!m_bNeedPap)
+            {
+                parBreak();
+                m_bNeedPap = true;
+            }
+            m_bNeedCr = true;
+        }
+    }
+    break;
+    case RTF_CHPGN:
+    {
+        OUString aStr("PAGE");
+        singleChar(cFieldStart);
+        text(aStr);
+        singleChar(cFieldSep, true);
+        singleChar(cFieldEnd);
+    }
+    break;
+    case RTF_CHFTNSEP:
+    {
+        static const sal_Unicode uFtnEdnSep = 0x3;
+        Mapper().utext(reinterpret_cast<const sal_uInt8*>(&uFtnEdnSep), 1);
+    }
+    break;
+    default:
+    {
+        SAL_INFO("writerfilter", "TODO handle symbol '" << keywordToString(nKeyword) << "'");
+        aSkip.setParsed(false);
+    }
+    break;
+    }
+    return RTFError::OK;
+}
+
+} // namespace rtftok
+} // namespace writerfilter
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/rtftok/rtfdispatchvalue.cxx b/writerfilter/source/rtftok/rtfdispatchvalue.cxx
new file mode 100644
index 0000000..76e24dd
--- /dev/null
+++ b/writerfilter/source/rtftok/rtfdispatchvalue.cxx
@@ -0,0 +1,1347 @@
+/* -*- 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 <com/sun/star/text/WrapTextMode.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/tencinfo.h>
+#include <tools/colordata.hxx>
+#include <tools/mapunit.hxx>
+
+#include <ooxml/resourceids.hxx>
+
+#include <rtfcharsets.hxx>
+#include <rtffly.hxx>
+#include <rtfreferenceproperties.hxx>
+#include <rtfskipdestination.hxx>
+
+using namespace com::sun::star;
+
+namespace writerfilter
+{
+
+int getNumberFormat(int nParam)
+{
+    static const int aMap[] =
+    {
+        NS_ooxml::LN_Value_ST_NumberFormat_decimal,
+        NS_ooxml::LN_Value_ST_NumberFormat_upperRoman,
+        NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman,
+        NS_ooxml::LN_Value_ST_NumberFormat_upperLetter,
+        NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter,
+        NS_ooxml::LN_Value_ST_NumberFormat_ordinal,
+        NS_ooxml::LN_Value_ST_NumberFormat_cardinalText,
+        NS_ooxml::LN_Value_ST_NumberFormat_ordinalText,
+        NS_ooxml::LN_Value_ST_NumberFormat_none,     // Undefined in RTF 1.8 spec.
+        NS_ooxml::LN_Value_ST_NumberFormat_none,     // Undefined in RTF 1.8 spec.
+        NS_ooxml::LN_Value_ST_NumberFormat_ideographDigital,
+        NS_ooxml::LN_Value_ST_NumberFormat_japaneseCounting,
+        NS_ooxml::LN_Value_ST_NumberFormat_aiueo,
+        NS_ooxml::LN_Value_ST_NumberFormat_iroha,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalFullWidth,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalHalfWidth,
+        NS_ooxml::LN_Value_ST_NumberFormat_japaneseLegal,
+        NS_ooxml::LN_Value_ST_NumberFormat_japaneseDigitalTenThousand ,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedCircleChinese,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalFullWidth2,
+        NS_ooxml::LN_Value_ST_NumberFormat_aiueoFullWidth,
+        NS_ooxml::LN_Value_ST_NumberFormat_irohaFullWidth,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalZero,
+        NS_ooxml::LN_Value_ST_NumberFormat_bullet,
+        NS_ooxml::LN_Value_ST_NumberFormat_ganada,
+        NS_ooxml::LN_Value_ST_NumberFormat_chosung,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedFullstop,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedParen,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedCircleChinese,
+        NS_ooxml::LN_Value_ST_NumberFormat_ideographEnclosedCircle,
+        NS_ooxml::LN_Value_ST_NumberFormat_ideographTraditional,
+        NS_ooxml::LN_Value_ST_NumberFormat_ideographZodiac,
+        NS_ooxml::LN_Value_ST_NumberFormat_ideographZodiacTraditional,
+        NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseCounting,
+        NS_ooxml::LN_Value_ST_NumberFormat_ideographLegalTraditional,
+        NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseCountingThousand,
+        NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseDigital,
+        NS_ooxml::LN_Value_ST_NumberFormat_chineseCounting,
+        NS_ooxml::LN_Value_ST_NumberFormat_chineseLegalSimplified,
+        NS_ooxml::LN_Value_ST_NumberFormat_chineseCountingThousand,
+        NS_ooxml::LN_Value_ST_NumberFormat_decimal,
+        NS_ooxml::LN_Value_ST_NumberFormat_koreanDigital,
+        NS_ooxml::LN_Value_ST_NumberFormat_koreanCounting,
+        NS_ooxml::LN_Value_ST_NumberFormat_koreanLegal,
+        NS_ooxml::LN_Value_ST_NumberFormat_koreanDigital2,
+        NS_ooxml::LN_Value_ST_NumberFormat_hebrew1,
+        NS_ooxml::LN_Value_ST_NumberFormat_arabicAlpha,
+        NS_ooxml::LN_Value_ST_NumberFormat_hebrew2,
+        NS_ooxml::LN_Value_ST_NumberFormat_arabicAbjad
+    };
+    const int nLen = SAL_N_ELEMENTS(aMap);
+    int nValue = 0;
+    if (nParam >= 0 && nParam < nLen)
+        nValue = aMap[nParam];
+    else  // 255 and the other cases.
+        nValue = NS_ooxml::LN_Value_ST_NumberFormat_none;
+    return nValue;
+}
+
+namespace rtftok
+{
+
+RTFError RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam)
+{
+    setNeedSect(true);
+    checkUnicode(/*bUnicode =*/ nKeyword != RTF_U, /*bHex =*/ true);
+    RTFSkipDestination aSkip(*this);
+    int nSprm = 0;
+    auto pIntValue = std::make_shared<RTFValue>(nParam);
+    // Trivial table sprms.
+    switch (nKeyword)
+    {
+    case RTF_LEVELJC:
+    {
+        nSprm = NS_ooxml::LN_CT_Lvl_lvlJc;
+        int nValue = 0;
+        switch (nParam)
+        {
+        case 0:
+            nValue = NS_ooxml::LN_Value_ST_Jc_left;
+            break;
+        case 1:
+            nValue = NS_ooxml::LN_Value_ST_Jc_center;
+            break;
+        case 2:
+            nValue = NS_ooxml::LN_Value_ST_Jc_right;
+            break;
+        }
+        pIntValue = std::make_shared<RTFValue>(nValue);
+        break;
+    }
+    case RTF_LEVELNFC:
+        nSprm = NS_ooxml::LN_CT_Lvl_numFmt;
+        pIntValue = std::make_shared<RTFValue>(getNumberFormat(nParam));
+        break;
+    case RTF_LEVELSTARTAT:
+        nSprm = NS_ooxml::LN_CT_Lvl_start;
+        break;
+    case RTF_LEVELPICTURE:
+        nSprm = NS_ooxml::LN_CT_Lvl_lvlPicBulletId;
+        break;
+    case RTF_SBASEDON:
+        nSprm = NS_ooxml::LN_CT_Style_basedOn;
+        pIntValue = std::make_shared<RTFValue>(getStyleName(nParam));
+        break;
+    default:
+        break;
+    }
+    if (nSprm > 0)
+    {
+        m_aStates.top().aTableSprms.set(nSprm, pIntValue);
+        return RTFError::OK;
+    }
+    // Trivial character sprms.
+    switch (nKeyword)
+    {
+    case RTF_FS:
+    case RTF_AFS:
+        nSprm = (m_aStates.top().isRightToLeft || m_aStates.top().eRunType == RTFParserState::HICH) ? NS_ooxml::LN_EG_RPrBase_szCs : NS_ooxml::LN_EG_RPrBase_sz;
+        break;
+    case RTF_ANIMTEXT:
+        nSprm = NS_ooxml::LN_EG_RPrBase_effect;
+        break;
+    case RTF_EXPNDTW:
+        nSprm = NS_ooxml::LN_EG_RPrBase_spacing;
+        break;
+    case RTF_KERNING:
+        nSprm = NS_ooxml::LN_EG_RPrBase_kern;
+        break;
+    case RTF_CHARSCALEX:
+        nSprm = NS_ooxml::LN_EG_RPrBase_w;
+        break;
+    default:
+        break;
+    }
+    if (nSprm > 0)
+    {
+        m_aStates.top().aCharacterSprms.set(nSprm, pIntValue);
+        return RTFError::OK;
+    }
+    // Trivial character attributes.
+    switch (nKeyword)
+    {
+    case RTF_LANG:
+    case RTF_ALANG:
+        if (m_aStates.top().isRightToLeft || m_aStates.top().eRunType == RTFParserState::HICH)
+        {
+            nSprm = NS_ooxml::LN_CT_Language_bidi;
+        }
+        else if (m_aStates.top().eRunType == RTFParserState::DBCH)
+        {
+            nSprm = NS_ooxml::LN_CT_Language_eastAsia;
+        }
+        else
+        {
+            assert(m_aStates.top().eRunType == RTFParserState::LOCH);
+            nSprm = NS_ooxml::LN_CT_Language_val;
+        }
+        break;
+    case RTF_LANGFE: // this one is always CJK apparently
+        nSprm = NS_ooxml::LN_CT_Language_eastAsia;
+        break;
+    default:
+        break;
+    }
+    if (nSprm > 0)
+    {
+        LanguageTag aTag((LanguageType)nParam);
+        auto pValue = std::make_shared<RTFValue>(aTag.getBcp47());
+        putNestedAttribute(m_aStates.top().aCharacterSprms, NS_ooxml::LN_EG_RPrBase_lang, nSprm, pValue);
+        // Language is a character property, but we should store it at a paragraph level as well for fields.
+        if (nKeyword == RTF_LANG && m_bNeedPap)
+            putNestedAttribute(m_aStates.top().aParagraphSprms, NS_ooxml::LN_EG_RPrBase_lang, nSprm, pValue);
+        return RTFError::OK;
+    }
+    // Trivial paragraph sprms.
+    switch (nKeyword)
+    {
+    case RTF_ITAP:
+        nSprm = NS_ooxml::LN_tblDepth;
+        break;
+    default:
+        break;
+    }
+    if (nSprm > 0)
+    {
+        m_aStates.top().aParagraphSprms.set(nSprm, pIntValue);
+        if (nKeyword == RTF_ITAP && nParam > 0)
+        {
+            while (m_aTableBufferStack.size() < sal::static_int_cast<std::size_t>(nParam))
+            {
+                m_aTableBufferStack.push_back(RTFBuffer_t());
+            }
+            // Invalid tables may omit INTBL after ITAP
+            dispatchFlag(RTF_INTBL); // sets newly pushed buffer as current
+            assert(m_aStates.top().pCurrentBuffer == &m_aTableBufferStack.back());
+        }
+        return RTFError::OK;
+    }
+
+    // Info group.
+    switch (nKeyword)
+    {
+    case RTF_YR:
+    {
+        m_aStates.top().nYear = nParam;
+        nSprm = 1;
+    }
+    break;
+    case RTF_MO:
+    {
+        m_aStates.top().nMonth = nParam;
+        nSprm = 1;
+    }
+    break;
+    case RTF_DY:
+    {
+        m_aStates.top().nDay = nParam;
+        nSprm = 1;
+    }
+    break;
+    case RTF_HR:
+    {
+        m_aStates.top().nHour = nParam;
+        nSprm = 1;
+    }
+    break;
+    case RTF_MIN:
+    {
+        m_aStates.top().nMinute = nParam;
+        nSprm = 1;
+    }
+    break;
+    default:
+        break;
+    }
+    if (nSprm > 0)
+        return RTFError::OK;
+
+    // Frame size / position.
+    Id nId = 0;
+    switch (nKeyword)
+    {
+    case RTF_ABSW:
+        nId = NS_ooxml::LN_CT_FramePr_w;
+        break;
+    case RTF_ABSH:
+        nId = NS_ooxml::LN_CT_FramePr_h;
+        break;
+    case RTF_POSX:
+    {
+        nId = NS_ooxml::LN_CT_FramePr_x;
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_xAlign, 0);
+    }
+    break;
+    case RTF_POSY:
+    {
+        nId = NS_ooxml::LN_CT_FramePr_y;
+        m_aStates.top().aFrame.setSprm(NS_ooxml::LN_CT_FramePr_yAlign, 0);
+    }
+    break;
+    default:
+        break;
+    }
+
+    if (nId > 0)
+    {
+        m_bNeedPap = true;
+        // Don't try to support text frames inside tables for now.
+        if (m_aStates.top().pCurrentBuffer != &m_aTableBufferStack.back())
+            m_aStates.top().aFrame.setSprm(nId, nParam);
+
+        return RTFError::OK;
+    }
+
+    // Then check for the more complex ones.
+    switch (nKeyword)
+    {
+    case RTF_F:
+    case RTF_AF:
+        if (m_aStates.top().isRightToLeft || m_aStates.top().eRunType == RTFParserState::HICH)
+        {
+            nSprm = NS_ooxml::LN_CT_Fonts_cs;
+        }
+        else if (m_aStates.top().eRunType == RTFParserState::DBCH)
+        {
+            nSprm = NS_ooxml::LN_CT_Fonts_eastAsia;
+        }
+        else
+        {
+            assert(m_aStates.top().eRunType == RTFParserState::LOCH);
+            nSprm = NS_ooxml::LN_CT_Fonts_ascii;
+        }
+        if (m_aStates.top().eDestination == Destination::FONTTABLE || m_aStates.top().eDestination == Destination::FONTENTRY)
+        {
+            m_aFontIndexes.push_back(nParam);
+            m_nCurrentFontIndex = getFontIndex(nParam);
+        }
+        else if (m_aStates.top().eDestination == Destination::LISTLEVEL)
+        {
+            RTFSprms aFontAttributes;
+            aFontAttributes.set(nSprm, std::make_shared<RTFValue>(m_aFontNames[getFontIndex(nParam)]));
+            RTFSprms aRunPropsSprms;
+            aRunPropsSprms.set(NS_ooxml::LN_EG_RPrBase_rFonts, std::make_shared<RTFValue>(aFontAttributes));
+            m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Lvl_rPr, std::make_shared<RTFValue>(RTFSprms(), aRunPropsSprms), RTFOverwrite::NO_APPEND);
+        }
+        else
+        {
+            m_nCurrentFontIndex = getFontIndex(nParam);
+            auto pValue = std::make_shared<RTFValue>(getFontName(m_nCurrentFontIndex));
+            putNestedAttribute(m_aStates.top().aCharacterSprms, NS_ooxml::LN_EG_RPrBase_rFonts, nSprm, pValue);
+            if (nKeyword == RTF_F)
+                m_aStates.top().nCurrentEncoding = getEncoding(m_nCurrentFontIndex);
+        }
+        break;
+    case RTF_RED:
+        m_aStates.top().aCurrentColor.nRed = nParam;
+        break;
+    case RTF_GREEN:
+        m_aStates.top().aCurrentColor.nGreen = nParam;
+        break;
+    case RTF_BLUE:
+        m_aStates.top().aCurrentColor.nBlue = nParam;
+        break;
+    case RTF_FCHARSET:
+    {
+        // we always send text to the domain mapper in OUString, so no
+        // need to send encoding info
+        int i;
+        for (i = 0; i < nRTFEncodings; i++)
+        {
+            if (aRTFEncodings[i].charset == nParam)
+                break;
+        }
+        if (i == nRTFEncodings)
+            // not found
+            return RTFError::OK;
+
+        m_nCurrentEncoding = rtl_getTextEncodingFromWindowsCodePage(aRTFEncodings[i].codepage);
+        m_aStates.top().nCurrentEncoding = m_nCurrentEncoding;
+    }
+    break;
+    case RTF_ANSICPG:
+    {
+        m_aDefaultState.nCurrentEncoding = rtl_getTextEncodingFromWindowsCodePage(nParam);
+        m_aStates.top().nCurrentEncoding = rtl_getTextEncodingFromWindowsCodePage(nParam);
+    }
+    break;
+    case RTF_CPG:
+        m_nCurrentEncoding = rtl_getTextEncodingFromWindowsCodePage(nParam);
+        m_aStates.top().nCurrentEncoding = m_nCurrentEncoding;
+        break;
+    case RTF_CF:
+    {
+        RTFSprms aAttributes;
+        auto pValue = std::make_shared<RTFValue>(getColorTable(nParam));
+        aAttributes.set(NS_ooxml::LN_CT_Color_val, pValue);
+        m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_color, std::make_shared<RTFValue>(aAttributes));
+    }
+    break;
+    case RTF_S:
+    {
+        m_aStates.top().nCurrentStyleIndex = nParam;
+
+        if (m_aStates.top().eDestination == Destination::STYLESHEET || m_aStates.top().eDestination == Destination::STYLEENTRY)
+        {
+            m_nCurrentStyleIndex = nParam;
+            auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_StyleType_paragraph);
+            m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Style_type, pValue); // paragraph style
+        }
+        else
+        {
+            OUString aName = getStyleName(nParam);
+            if (!aName.isEmpty())
+            {
+                if (m_aStates.top().eDestination == Destination::LISTLEVEL)
+                    m_aStates.top().aTableSprms.set(NS_ooxml::LN_CT_Lvl_pStyle, std::make_shared<RTFValue>(aName));
+                else
+                    m_aStates.top().aParagraphSprms.set(NS_ooxml::LN_CT_PPrBase_pStyle, std::make_shared<RTFValue>(aName));
+
+            }
+        }
+    }
+    break;
+    case RTF_CS:
+        m_aStates.top().nCurrentCharacterStyleIndex = nParam;
+        if (m_aStates.top().eDestination == Destination::STYLESHEET || m_aStates.top().eDestination == Destination::STYLEENTRY)
+        {
+            m_nCurrentStyleIndex = nParam;
+            auto pValue = std::make_shared<RTFValue>(NS_ooxml::LN_Value_ST_StyleType_character);
+            m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Style_type, pValue); // character style
+        }
+        else
+        {
+            OUString aName = getStyleName(nParam);
+            if (!aName.isEmpty())
+                m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_rStyle, std::make_shared<RTFValue>(aName));
+        }
+        break;
+    case RTF_DS:
+        if (m_aStates.top().eDestination == Destination::STYLESHEET || m_aStates.top().eDestination == Destination::STYLEENTRY)
+        {
+            m_nCurrentStyleIndex = nParam;
+            auto pValue = std::make_shared<RTFValue>(0); // TODO no value in enum StyleType?
+            m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Style_type, pValue); // section style
+        }
+        break;
+    case RTF_TS:
+        if (m_aStates.top().eDestination == Destination::STYLESHEET || m_aStates.top().eDestination == Destination::STYLEENTRY)
+        {
+            m_nCurrentStyleIndex = nParam;
+            // FIXME the correct value would be NS_ooxml::LN_Value_ST_StyleType_table but maybe table styles mess things up in dmapper, be cautious and disable them for now
+            auto pValue = std::make_shared<RTFValue>(0);
+            m_aStates.top().aTableAttributes.set(NS_ooxml::LN_CT_Style_type, pValue); // table style
+        }
+        break;
+    case RTF_DEFF:
+        m_nDefaultFontIndex = nParam;
+        break;
+    case RTF_DEFLANG:
+    case RTF_ADEFLANG:
+    {
+        LanguageTag aTag((LanguageType)nParam);
+        auto pValue = std::make_shared<RTFValue>(aTag.getBcp47());
+        putNestedAttribute(m_aStates.top().aCharacterSprms, (nKeyword == RTF_DEFLANG ? NS_ooxml::LN_EG_RPrBase_lang : NS_ooxml::LN_CT_Language_bidi), nSprm, pValue);
+    }
+    break;
+    case RTF_CHCBPAT:
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam ? getColorTable(nParam) : COL_AUTO);
+        putNestedAttribute(m_aStates.top().aCharacterSprms, NS_ooxml::LN_EG_RPrBase_shd, NS_ooxml::LN_CT_Shd_fill, pValue);
+    }
+    break;
+    case RTF_CLCBPAT:
+    {
+        auto pValue = std::make_shared<RTFValue>(getColorTable(nParam));
+        putNestedAttribute(m_aStates.top().aTableCellSprms, NS_ooxml::LN_CT_TcPrBase_shd, NS_ooxml::LN_CT_Shd_fill, pValue);
+    }
+    break;
+    case RTF_CBPAT:
+        if (nParam)
+        {
+            auto pValue = std::make_shared<RTFValue>(getColorTable(nParam));
+            putNestedAttribute(m_aStates.top().aParagraphSprms, NS_ooxml::LN_CT_PrBase_shd, NS_ooxml::LN_CT_Shd_fill, pValue);
+        }
+        break;
+    case RTF_ULC:
+    {
+        auto pValue = std::make_shared<RTFValue>(getColorTable(nParam));
+        m_aStates.top().aCharacterSprms.set(0x6877, pValue);
+    }
+    break;
+    case RTF_HIGHLIGHT:
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam ? getColorTable(nParam) : COL_AUTO);
+        m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_highlight, pValue);
+    }
+    break;
+    case RTF_UP:
+    case RTF_DN:
+    {
+        auto pValue = std::make_shared<RTFValue>(nParam * (nKeyword == RTF_UP ? 1 : -1));
+        m_aStates.top().aCharacterSprms.set(NS_ooxml::LN_EG_RPrBase_position, pValue);
+    }
+    break;
+    case RTF_HORZVERT:
+    {
+        auto pValue = std::make_shared<RTFValue>(int(true));
+        m_aStates.top().aCharacterAttributes.set(NS_ooxml::LN_CT_EastAsianLayout_vert, pValue);
+        if (nParam)
+            // rotate fits to a single line

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list