summaryrefslogtreecommitdiffstats
path: root/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'writerfilter/source/rtftok/rtfdispatchsymbol.cxx')
-rw-r--r--writerfilter/source/rtftok/rtfdispatchsymbol.cxx442
1 files changed, 442 insertions, 0 deletions
diff --git a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
new file mode 100644
index 000000000..3f9ed20bf
--- /dev/null
+++ b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx
@@ -0,0 +1,442 @@
+/* -*- 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/io/WrongFormatException.hpp>
+#include <svl/lngmisc.hxx>
+
+#include <ooxml/resourceids.hxx>
+
+#include <sal/log.hxx>
+
+#include "rtfreferenceproperties.hxx"
+#include "rtfskipdestination.hxx"
+
+using namespace com::sun::star;
+
+namespace writerfilter::rtftok
+{
+RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
+{
+ setNeedSect(true);
+ if (nKeyword != RTFKeyword::HEXCHAR)
+ checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
+ else
+ checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
+ RTFSkipDestination aSkip(*this);
+
+ if (RTFKeyword::LINE == nKeyword)
+ {
+ // very special handling since text() will eat lone '\n'
+ singleChar('\n', /*bRunProps=*/true);
+ return RTFError::OK;
+ }
+ // Trivial symbols
+ sal_uInt8 cCh = 0;
+ switch (nKeyword)
+ {
+ case RTFKeyword::TAB:
+ cCh = '\t';
+ break;
+ case RTFKeyword::BACKSLASH:
+ cCh = '\\';
+ break;
+ case RTFKeyword::LBRACE:
+ cCh = '{';
+ break;
+ case RTFKeyword::RBRACE:
+ cCh = '}';
+ break;
+ case RTFKeyword::EMDASH:
+ cCh = 151;
+ break;
+ case RTFKeyword::ENDASH:
+ cCh = 150;
+ break;
+ case RTFKeyword::BULLET:
+ cCh = 149;
+ break;
+ case RTFKeyword::LQUOTE:
+ cCh = 145;
+ break;
+ case RTFKeyword::RQUOTE:
+ cCh = 146;
+ break;
+ case RTFKeyword::LDBLQUOTE:
+ cCh = 147;
+ break;
+ case RTFKeyword::RDBLQUOTE:
+ cCh = 148;
+ break;
+ default:
+ break;
+ }
+ if (cCh > 0)
+ {
+ OUString aStr(OStringToOUString(OStringChar(char(cCh)), RTL_TEXTENCODING_MS_1252));
+ text(aStr);
+ return RTFError::OK;
+ }
+
+ switch (nKeyword)
+ {
+ case RTFKeyword::IGNORE:
+ {
+ m_bSkipUnknown = true;
+ aSkip.setReset(false);
+ return RTFError::OK;
+ }
+ break;
+ case RTFKeyword::PAR:
+ {
+ if (m_aStates.top().getDestination() == 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().getCurrentBuffer())
+ {
+ 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, new RTFValue(1));
+ writerfilter::Reference<Properties>::Pointer_t pProperties
+ = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms));
+ Mapper().props(pProperties);
+ }
+ m_nCellxMax = 0;
+ }
+ else if (m_aStates.top().getDestination() != Destination::SHAPETEXT)
+ {
+ RTFValue::Pointer_t pValue;
+ m_aStates.top().getCurrentBuffer()->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().getFrame().inFrame())
+ m_bNeedPar = false;
+ m_bNeedFinalPar = false;
+ }
+ break;
+ case RTFKeyword::SECT:
+ {
+ if (m_bNeedCr)
+ dispatchSymbol(RTFKeyword::PAR);
+
+ m_bHadSect = true;
+ if (m_bIgnoreNextContSectBreak)
+ m_bIgnoreNextContSectBreak = false;
+ else
+ {
+ sectBreak();
+ if (m_nResetBreakOnSectBreak != RTFKeyword::invalid)
+ {
+ // this should run on _second_ \sect after \page
+ dispatchSymbol(m_nResetBreakOnSectBreak); // lazy reset
+ m_nResetBreakOnSectBreak = RTFKeyword::invalid;
+ m_bNeedSect = false; // dispatchSymbol set it
+ }
+ }
+ }
+ break;
+ case RTFKeyword::NOBREAK:
+ {
+ OUString aStr(SVT_HARD_SPACE);
+ text(aStr);
+ }
+ break;
+ case RTFKeyword::NOBRKHYPH:
+ {
+ OUString aStr(SVT_HARD_HYPHEN);
+ text(aStr);
+ }
+ break;
+ case RTFKeyword::OPTHYPH:
+ {
+ OUString aStr(SVT_SOFT_HYPHEN);
+ text(aStr);
+ }
+ break;
+ case RTFKeyword::HEXCHAR:
+ m_aStates.top().setInternalState(RTFInternalState::HEX);
+ break;
+ case RTFKeyword::CELL:
+ case RTFKeyword::NESTCELL:
+ {
+ if (nKeyword == RTFKeyword::CELL)
+ m_bAfterCellBeforeRow = true;
+
+ checkFirstRun();
+ if (m_bNeedPap)
+ {
+ // There were no runs in the cell, so we need to send paragraph and character properties here.
+ auto pPValue = new RTFValue(m_aStates.top().getParagraphAttributes(),
+ m_aStates.top().getParagraphSprms());
+ bufferProperties(m_aTableBufferStack.back(), pPValue, nullptr);
+ auto pCValue = new RTFValue(m_aStates.top().getCharacterAttributes(),
+ m_aStates.top().getCharacterSprms());
+ bufferProperties(m_aTableBufferStack.back(), pCValue, nullptr);
+ }
+
+ RTFValue::Pointer_t pValue;
+ m_aTableBufferStack.back().emplace_back(Buf_t(BUFFER_CELLEND, pValue, nullptr));
+ m_bNeedPap = true;
+ }
+ break;
+ case RTFKeyword::NESTROW:
+ {
+ tools::SvRef<TableRowBuffer> const pBuffer(
+ new TableRowBuffer(m_aTableBufferStack.back(), m_aNestedTableCellsSprms,
+ m_aNestedTableCellsAttributes, m_nNestedCells));
+ prepareProperties(m_aStates.top(), pBuffer->GetParaProperties(),
+ pBuffer->GetFrameProperties(), pBuffer->GetRowProperties(),
+ m_nNestedCells, m_nNestedCurrentCellX - m_nNestedTRLeft);
+
+ if (m_aTableBufferStack.size() == 1 || !m_aStates.top().getCurrentBuffer())
+ {
+ throw io::WrongFormatException("mismatch between \\itap and number of \\nestrow",
+ nullptr);
+ }
+ assert(m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back());
+ // 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].getCurrentBuffer() == &m_aTableBufferStack.back())
+ {
+ m_aStates[i].setCurrentBuffer(
+ &m_aTableBufferStack[m_aTableBufferStack.size() - 2]);
+ }
+ }
+ m_aTableBufferStack.pop_back();
+ m_aTableBufferStack.back().emplace_back(
+ Buf_t(BUFFER_NESTROW, RTFValue::Pointer_t(), pBuffer));
+
+ m_aNestedTableCellsSprms.clear();
+ m_aNestedTableCellsAttributes.clear();
+ m_nNestedCells = 0;
+ m_bNeedPap = true;
+ }
+ break;
+ case RTFKeyword::ROW:
+ {
+ m_bAfterCellBeforeRow = false;
+ if (m_aStates.top().getTableRowWidthAfter() > 0)
+ {
+ // Add fake cellx / cell, RTF equivalent of
+ // OOXMLFastContextHandlerTextTableRow::handleGridAfter().
+ auto pXValue = new RTFValue(m_aStates.top().getTableRowWidthAfter());
+ m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue,
+ RTFOverwrite::NO_APPEND);
+ dispatchSymbol(RTFKeyword::CELL);
+
+ // Adjust total width, which is done in the \cellx handler for normal cells.
+ m_nTopLevelCurrentCellX += m_aStates.top().getTableRowWidthAfter();
+
+ m_aStates.top().setTableRowWidthAfter(0);
+ }
+
+ 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().getTableRowSprms().find(
+ NS_ooxml::LN_CT_TblGridBase_gridCol, false);
+ const int nXValueLast = pXValueLast ? pXValueLast->getInt() : 0;
+ auto pXValue = new RTFValue(nXValueLast + m_nCellxMax - m_nTopLevelCurrentCellX);
+ m_aStates.top().getTableRowSprms().eraseLast(NS_ooxml::LN_CT_TblGridBase_gridCol);
+ m_aStates.top().getTableRowSprms().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].getCurrentBuffer() == &m_aTableBufferStack.back())
+ {
+ m_aStates[i].setCurrentBuffer(&m_aTableBufferStack.front());
+ }
+ }
+ m_aTableBufferStack.pop_back();
+ }
+
+ replayRowBuffer(m_aTableBufferStack.back(), m_aTopLevelTableCellsSprms,
+ m_aTopLevelTableCellsAttributes, m_nTopLevelCells);
+
+ // The scope of the table cell defaults is one row.
+ m_aDefaultState.getTableCellSprms().clear();
+ m_aStates.top().getTableCellSprms() = m_aDefaultState.getTableCellSprms();
+ m_aStates.top().getTableCellAttributes() = m_aDefaultState.getTableCellAttributes();
+
+ 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 - m_nTopLevelTRLeft);
+ 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 RTFKeyword::COLUMN:
+ {
+ bool bColumns = false; // If we have multiple columns
+ RTFValue::Pointer_t pCols
+ = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_cols);
+ if (pCols)
+ {
+ RTFValue::Pointer_t pNum = pCols->getAttributes().find(NS_ooxml::LN_CT_Columns_num);
+ if (pNum && pNum->getInt() > 1)
+ bColumns = true;
+ }
+ checkFirstRun();
+ if (bColumns)
+ {
+ sal_uInt8 const sBreak[] = { 0xe };
+ Mapper().startCharacterGroup();
+ Mapper().text(sBreak, 1);
+ Mapper().endCharacterGroup();
+ }
+ else
+ dispatchSymbol(RTFKeyword::PAGE);
+ }
+ break;
+ case RTFKeyword::CHFTN:
+ {
+ if (m_aStates.top().getCurrentBuffer() == &m_aSuperBuffer)
+ // Stop buffering, there will be no custom mark for this footnote or endnote.
+ m_aStates.top().setCurrentBuffer(nullptr);
+ break;
+ }
+ case RTFKeyword::PAGE:
+ {
+ // Ignore page breaks inside tables.
+ if (m_aStates.top().getCurrentBuffer() == &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().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
+ // Unless we're on a title page.
+ RTFValue::Pointer_t pTitlePg
+ = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_titlePg);
+ if (((pBreak
+ && pBreak->getInt()
+ == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous))
+ || m_nResetBreakOnSectBreak == RTFKeyword::SBKNONE)
+ && !(pTitlePg && pTitlePg->getInt()))
+ {
+ if (m_bWasInFrame)
+ {
+ dispatchSymbol(RTFKeyword::PAR);
+ m_bWasInFrame = false;
+ }
+ sectBreak();
+ // note: this will not affect the following section break
+ // but the one just pushed
+ dispatchFlag(RTFKeyword::SBKPAGE);
+ if (m_bNeedPar)
+ dispatchSymbol(RTFKeyword::PAR);
+ m_bIgnoreNextContSectBreak = true;
+ // arrange to clean up the synthetic RTFKeyword::SBKPAGE
+ m_nResetBreakOnSectBreak = RTFKeyword::SBKNONE;
+ }
+ else
+ {
+ bool bFirstRun = m_bFirstRun;
+ checkFirstRun();
+ checkNeedPap();
+ sal_uInt8 const sBreak[] = { 0xc };
+ Mapper().text(sBreak, 1);
+ if (bFirstRun || m_bNeedCr)
+ {
+ // If we don't have content in the document yet (so the break-before can't move
+ // to a second layout page) or we already have characters sent (so the paragraph
+ // properties are already finalized), then continue inserting a fake paragraph.
+ if (!m_bNeedPap)
+ {
+ parBreak();
+ m_bNeedPap = true;
+ }
+ }
+ m_bNeedCr = true;
+ }
+ }
+ break;
+ case RTFKeyword::CHPGN:
+ {
+ OUString aStr("PAGE");
+ singleChar(cFieldStart);
+ text(aStr);
+ singleChar(cFieldSep, true);
+ singleChar(cFieldEnd);
+ }
+ break;
+ case RTFKeyword::CHFTNSEP:
+ {
+ static const sal_Unicode uFtnEdnSep = 0x3;
+ Mapper().utext(reinterpret_cast<const sal_uInt8*>(&uFtnEdnSep), 1);
+ }
+ break;
+ default:
+ {
+ SAL_INFO("writerfilter.rtf",
+ "TODO handle symbol '" << keywordToString(nKeyword) << "'");
+ aSkip.setParsed(false);
+ }
+ break;
+ }
+ return RTFError::OK;
+}
+
+} // namespace writerfilter::rtftok
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */