Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / writerfilter / source / rtftok / rtfdispatchsymbol.cxx
blob69879fcd1b62098bb3c4a2e14a234fc67794c1fb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include "rtfdocumentimpl.hxx"
12 #include <com/sun/star/io/WrongFormatException.hpp>
13 #include <svl/lngmisc.hxx>
15 #include <ooxml/resourceids.hxx>
17 #include <sal/log.hxx>
19 #include "rtfreferenceproperties.hxx"
20 #include "rtfskipdestination.hxx"
22 using namespace com::sun::star;
24 namespace writerfilter
26 namespace rtftok
28 RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword)
30 setNeedSect(true);
31 if (nKeyword != RTF_HEXCHAR)
32 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
33 else
34 checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
35 RTFSkipDestination aSkip(*this);
37 if (RTF_LINE == nKeyword)
39 // very special handling since text() will eat lone '\n'
40 singleChar('\n');
41 return RTFError::OK;
43 // Trivial symbols
44 sal_uInt8 cCh = 0;
45 switch (nKeyword)
47 case RTF_TAB:
48 cCh = '\t';
49 break;
50 case RTF_BACKSLASH:
51 cCh = '\\';
52 break;
53 case RTF_LBRACE:
54 cCh = '{';
55 break;
56 case RTF_RBRACE:
57 cCh = '}';
58 break;
59 case RTF_EMDASH:
60 cCh = 151;
61 break;
62 case RTF_ENDASH:
63 cCh = 150;
64 break;
65 case RTF_BULLET:
66 cCh = 149;
67 break;
68 case RTF_LQUOTE:
69 cCh = 145;
70 break;
71 case RTF_RQUOTE:
72 cCh = 146;
73 break;
74 case RTF_LDBLQUOTE:
75 cCh = 147;
76 break;
77 case RTF_RDBLQUOTE:
78 cCh = 148;
79 break;
80 default:
81 break;
83 if (cCh > 0)
85 OUString aStr(OStringToOUString(OString(cCh), RTL_TEXTENCODING_MS_1252));
86 text(aStr);
87 return RTFError::OK;
90 switch (nKeyword)
92 case RTF_IGNORE:
94 m_bSkipUnknown = true;
95 aSkip.setReset(false);
96 return RTFError::OK;
98 break;
99 case RTF_PAR:
101 if (m_aStates.top().getDestination() == Destination::FOOTNOTESEPARATOR)
102 break; // just ignore it - only thing we read in here is CHFTNSEP
103 checkFirstRun();
104 bool bNeedPap = m_bNeedPap;
105 checkNeedPap();
106 if (bNeedPap)
107 runProps();
108 if (!m_aStates.top().getCurrentBuffer())
110 parBreak();
111 // Not in table? Reset max width.
112 if (m_nCellxMax)
114 // Was in table, but not anymore -> tblEnd.
115 RTFSprms aAttributes;
116 RTFSprms aSprms;
117 aSprms.set(NS_ooxml::LN_tblEnd, new RTFValue(1));
118 writerfilter::Reference<Properties>::Pointer_t pProperties
119 = new RTFReferenceProperties(aAttributes, aSprms);
120 Mapper().props(pProperties);
122 m_nCellxMax = 0;
124 else if (m_aStates.top().getDestination() != Destination::SHAPETEXT)
126 RTFValue::Pointer_t pValue;
127 m_aStates.top().getCurrentBuffer()->push_back(Buf_t(BUFFER_PAR, pValue, nullptr));
129 // but don't emit properties yet, since they may change till the first text token arrives
130 m_bNeedPap = true;
131 if (!m_aStates.top().getFrame().inFrame())
132 m_bNeedPar = false;
133 m_bNeedFinalPar = false;
135 break;
136 case RTF_SECT:
138 m_bHadSect = true;
139 if (m_bIgnoreNextContSectBreak)
140 m_bIgnoreNextContSectBreak = false;
141 else
143 sectBreak();
144 if (m_nResetBreakOnSectBreak != RTF_invalid)
146 // this should run on _second_ \sect after \page
147 dispatchSymbol(m_nResetBreakOnSectBreak); // lazy reset
148 m_nResetBreakOnSectBreak = RTF_invalid;
149 m_bNeedSect = false; // dispatchSymbol set it
153 break;
154 case RTF_NOBREAK:
156 OUString aStr(SVT_HARD_SPACE);
157 text(aStr);
159 break;
160 case RTF_NOBRKHYPH:
162 OUString aStr(SVT_HARD_HYPHEN);
163 text(aStr);
165 break;
166 case RTF_OPTHYPH:
168 OUString aStr(SVT_SOFT_HYPHEN);
169 text(aStr);
171 break;
172 case RTF_HEXCHAR:
173 m_aStates.top().setInternalState(RTFInternalState::HEX);
174 break;
175 case RTF_CELL:
176 case RTF_NESTCELL:
178 if (nKeyword == RTF_CELL)
179 m_bAfterCellBeforeRow = true;
181 checkFirstRun();
182 if (m_bNeedPap)
184 // There were no runs in the cell, so we need to send paragraph and character properties here.
185 auto pPValue = new RTFValue(m_aStates.top().getParagraphAttributes(),
186 m_aStates.top().getParagraphSprms());
187 bufferProperties(m_aTableBufferStack.back(), pPValue, nullptr);
188 auto pCValue = new RTFValue(m_aStates.top().getCharacterAttributes(),
189 m_aStates.top().getCharacterSprms());
190 bufferProperties(m_aTableBufferStack.back(), pCValue, nullptr);
193 RTFValue::Pointer_t pValue;
194 m_aTableBufferStack.back().emplace_back(Buf_t(BUFFER_CELLEND, pValue, nullptr));
195 m_bNeedPap = true;
197 break;
198 case RTF_NESTROW:
200 tools::SvRef<TableRowBuffer> const pBuffer(
201 new TableRowBuffer(m_aTableBufferStack.back(), m_aNestedTableCellsSprms,
202 m_aNestedTableCellsAttributes, m_nNestedCells));
203 prepareProperties(m_aStates.top(), pBuffer->GetParaProperties(),
204 pBuffer->GetFrameProperties(), pBuffer->GetRowProperties(),
205 m_nNestedCells, m_nNestedCurrentCellX - m_nNestedTRLeft);
207 if (m_aTableBufferStack.size() == 1 || !m_aStates.top().getCurrentBuffer())
209 throw io::WrongFormatException("mismatch between \\itap and number of \\nestrow",
210 nullptr);
212 assert(m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back());
213 // note: there may be several states pointing to table buffer!
214 for (std::size_t i = 0; i < m_aStates.size(); ++i)
216 if (m_aStates[i].getCurrentBuffer() == &m_aTableBufferStack.back())
218 m_aStates[i].setCurrentBuffer(
219 &m_aTableBufferStack[m_aTableBufferStack.size() - 2]);
222 m_aTableBufferStack.pop_back();
223 m_aTableBufferStack.back().emplace_back(
224 Buf_t(BUFFER_NESTROW, RTFValue::Pointer_t(), pBuffer));
226 m_aNestedTableCellsSprms.clear();
227 m_aNestedTableCellsAttributes.clear();
228 m_nNestedCells = 0;
229 m_bNeedPap = true;
231 break;
232 case RTF_ROW:
234 m_bAfterCellBeforeRow = false;
235 if (m_aStates.top().getTableRowWidthAfter() > 0)
237 // Add fake cellx / cell, RTF equivalent of
238 // OOXMLFastContextHandlerTextTableRow::handleGridAfter().
239 auto pXValue = new RTFValue(m_aStates.top().getTableRowWidthAfter());
240 m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue,
241 RTFOverwrite::NO_APPEND);
242 dispatchSymbol(RTF_CELL);
244 // Adjust total width, which is done in the \cellx handler for normal cells.
245 m_nTopLevelCurrentCellX += m_aStates.top().getTableRowWidthAfter();
247 m_aStates.top().setTableRowWidthAfter(0);
250 bool bRestored = false;
251 // Ending a row, but no cells defined?
252 // See if there was an invalid table row reset, so we can restore cell infos to help invalid documents.
253 if (!m_nTopLevelCurrentCellX && m_nBackupTopLevelCurrentCellX)
255 restoreTableRowProperties();
256 bRestored = true;
259 // 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
260 const int MINLAY = 23; // sw/inc/swtypes.hxx, minimal possible size of frames.
261 if ((m_nCellxMax - m_nTopLevelCurrentCellX) >= MINLAY)
263 auto pXValueLast = m_aStates.top().getTableRowSprms().find(
264 NS_ooxml::LN_CT_TblGridBase_gridCol, false);
265 const int nXValueLast = pXValueLast ? pXValueLast->getInt() : 0;
266 auto pXValue = new RTFValue(nXValueLast + m_nCellxMax - m_nTopLevelCurrentCellX);
267 m_aStates.top().getTableRowSprms().eraseLast(NS_ooxml::LN_CT_TblGridBase_gridCol);
268 m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue,
269 RTFOverwrite::NO_APPEND);
270 m_nTopLevelCurrentCellX = m_nCellxMax;
273 if (m_nTopLevelCells)
275 // Make a backup before we start popping elements
276 m_aTableInheritingCellsSprms = m_aTopLevelTableCellsSprms;
277 m_aTableInheritingCellsAttributes = m_aTopLevelTableCellsAttributes;
278 m_nInheritingCells = m_nTopLevelCells;
280 else
282 // No table definition? Then inherit from the previous row
283 m_aTopLevelTableCellsSprms = m_aTableInheritingCellsSprms;
284 m_aTopLevelTableCellsAttributes = m_aTableInheritingCellsAttributes;
285 m_nTopLevelCells = m_nInheritingCells;
288 while (m_aTableBufferStack.size() > 1)
290 SAL_WARN("writerfilter.rtf", "dropping extra table buffer");
291 // note: there may be several states pointing to table buffer!
292 for (std::size_t i = 0; i < m_aStates.size(); ++i)
294 if (m_aStates[i].getCurrentBuffer() == &m_aTableBufferStack.back())
296 m_aStates[i].setCurrentBuffer(&m_aTableBufferStack.front());
299 m_aTableBufferStack.pop_back();
302 replayRowBuffer(m_aTableBufferStack.back(), m_aTopLevelTableCellsSprms,
303 m_aTopLevelTableCellsAttributes, m_nTopLevelCells);
305 // The scope of the table cell defaults is one row.
306 m_aDefaultState.getTableCellSprms().clear();
307 m_aStates.top().getTableCellSprms() = m_aDefaultState.getTableCellSprms();
308 m_aStates.top().getTableCellAttributes() = m_aDefaultState.getTableCellAttributes();
310 writerfilter::Reference<Properties>::Pointer_t paraProperties;
311 writerfilter::Reference<Properties>::Pointer_t frameProperties;
312 writerfilter::Reference<Properties>::Pointer_t rowProperties;
313 prepareProperties(m_aStates.top(), paraProperties, frameProperties, rowProperties,
314 m_nTopLevelCells, m_nTopLevelCurrentCellX - m_nTopLevelTRLeft);
315 sendProperties(paraProperties, frameProperties, rowProperties);
317 m_bNeedPap = true;
318 m_bNeedFinalPar = true;
319 m_aTableBufferStack.back().clear();
320 m_nTopLevelCells = 0;
322 if (bRestored)
323 // We restored cell definitions, clear these now.
324 // This is necessary, as later cell definitions want to overwrite the restored ones.
325 resetTableRowProperties();
327 break;
328 case RTF_COLUMN:
330 bool bColumns = false; // If we have multiple columns
331 RTFValue::Pointer_t pCols
332 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_cols);
333 if (pCols)
335 RTFValue::Pointer_t pNum = pCols->getAttributes().find(NS_ooxml::LN_CT_Columns_num);
336 if (pNum.get() && pNum->getInt() > 1)
337 bColumns = true;
339 checkFirstRun();
340 if (bColumns)
342 sal_uInt8 const sBreak[] = { 0xe };
343 Mapper().startCharacterGroup();
344 Mapper().text(sBreak, 1);
345 Mapper().endCharacterGroup();
347 else
348 dispatchSymbol(RTF_PAGE);
350 break;
351 case RTF_CHFTN:
353 if (m_aStates.top().getCurrentBuffer() == &m_aSuperBuffer)
354 // Stop buffering, there will be no custom mark for this footnote or endnote.
355 m_aStates.top().setCurrentBuffer(nullptr);
356 break;
358 case RTF_PAGE:
360 // Ignore page breaks inside tables.
361 if (m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back())
362 break;
364 // If we're inside a continuous section, we should send a section break, not a page one.
365 RTFValue::Pointer_t pBreak
366 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
367 // Unless we're on a title page.
368 RTFValue::Pointer_t pTitlePg
369 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_titlePg);
370 if (((pBreak.get()
371 && pBreak->getInt()
372 == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous))
373 || m_nResetBreakOnSectBreak == RTF_SBKNONE)
374 && !(pTitlePg.get() && pTitlePg->getInt()))
376 if (m_bWasInFrame)
378 dispatchSymbol(RTF_PAR);
379 m_bWasInFrame = false;
381 sectBreak();
382 // note: this will not affect the following section break
383 // but the one just pushed
384 dispatchFlag(RTF_SBKPAGE);
385 if (m_bNeedPar)
386 dispatchSymbol(RTF_PAR);
387 m_bIgnoreNextContSectBreak = true;
388 // arrange to clean up the synthetic RTF_SBKPAGE
389 m_nResetBreakOnSectBreak = RTF_SBKNONE;
391 else
393 checkFirstRun();
394 checkNeedPap();
395 sal_uInt8 const sBreak[] = { 0xc };
396 Mapper().text(sBreak, 1);
397 if (!m_bNeedPap)
399 parBreak();
400 m_bNeedPap = true;
402 m_bNeedCr = true;
405 break;
406 case RTF_CHPGN:
408 OUString aStr("PAGE");
409 singleChar(cFieldStart);
410 text(aStr);
411 singleChar(cFieldSep, true);
412 singleChar(cFieldEnd);
414 break;
415 case RTF_CHFTNSEP:
417 static const sal_Unicode uFtnEdnSep = 0x3;
418 Mapper().utext(reinterpret_cast<const sal_uInt8*>(&uFtnEdnSep), 1);
420 break;
421 default:
423 SAL_INFO("writerfilter.rtf",
424 "TODO handle symbol '" << keywordToString(nKeyword) << "'");
425 aSkip.setParsed(false);
427 break;
429 return RTFError::OK;
432 } // namespace rtftok
433 } // namespace writerfilter
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */