LibreOffice Module writerfilter (master) 1
rtfdispatchsymbol.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include "rtfdocumentimpl.hxx"
11
12#include <com/sun/star/io/WrongFormatException.hpp>
13#include <svl/lngmisc.hxx>
14
15#include <ooxml/resourceids.hxx>
16
17#include <sal/log.hxx>
18
21
22using namespace com::sun::star;
23
25{
27{
28 setNeedSect(true);
29 if (nKeyword != RTFKeyword::HEXCHAR)
30 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
31 else
32 checkUnicode(/*bUnicode =*/true, /*bHex =*/false);
33 RTFSkipDestination aSkip(*this);
34
35 if (RTFKeyword::LINE == nKeyword)
36 {
37 // very special handling since text() will eat lone '\n'
38 singleChar('\n', /*bRunProps=*/true);
39 return RTFError::OK;
40 }
41 // Trivial symbols
42 sal_uInt8 cCh = 0;
43 switch (nKeyword)
44 {
45 case RTFKeyword::TAB:
46 cCh = '\t';
47 break;
49 cCh = '\\';
50 break;
52 cCh = '{';
53 break;
55 cCh = '}';
56 break;
58 cCh = 151;
59 break;
61 cCh = 150;
62 break;
64 cCh = 149;
65 break;
67 cCh = 145;
68 break;
70 cCh = 146;
71 break;
73 cCh = 147;
74 break;
76 cCh = 148;
77 break;
78 default:
79 break;
80 }
81 if (cCh > 0)
82 {
83 OUString aStr(OStringToOUString(OStringChar(char(cCh)), RTL_TEXTENCODING_MS_1252));
84 text(aStr);
85 return RTFError::OK;
86 }
87
88 switch (nKeyword)
89 {
91 {
92 m_bSkipUnknown = true;
93 aSkip.setReset(false);
94 return RTFError::OK;
95 }
96 break;
97 case RTFKeyword::PAR:
98 {
100 break; // just ignore it - only thing we read in here is CHFTNSEP
102 checkNeedPap();
103 runProps(); // tdf#152872 paragraph marker formatting
105 {
106 parBreak();
107 // Not in table? Reset max width.
108 if (m_nCellxMax)
109 {
110 // Was in table, but not anymore -> tblEnd.
111 RTFSprms aAttributes;
112 RTFSprms aSprms;
113 aSprms.set(NS_ooxml::LN_tblEnd, new RTFValue(1));
115 = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms));
116 Mapper().props(pProperties);
117 }
118 m_nCellxMax = 0;
119 }
121 {
122 RTFValue::Pointer_t pValue;
123 m_aStates.top().getCurrentBuffer()->push_back(Buf_t(BUFFER_PAR, pValue, nullptr));
124 }
125 // but don't emit properties yet, since they may change till the first text token arrives
126 m_bNeedPap = true;
127 if (!m_aStates.top().getFrame().inFrame())
128 m_bNeedPar = false;
129 m_bNeedFinalPar = false;
130 }
131 break;
132 case RTFKeyword::SECT:
133 {
134 if (m_bNeedCr)
136
137 m_bHadSect = true;
140 else
141 {
142 sectBreak();
144 {
145 // this should run on _second_ \sect after \page
148 m_bNeedSect = false; // dispatchSymbol set it
149 }
150 }
151 }
152 break;
154 {
155 OUString aStr(SVT_HARD_SPACE);
156 text(aStr);
157 }
158 break;
160 {
161 OUString aStr(SVT_HARD_HYPHEN);
162 text(aStr);
163 }
164 break;
166 {
167 OUString aStr(SVT_SOFT_HYPHEN);
168 text(aStr);
169 }
170 break;
173 break;
174 case RTFKeyword::CELL:
176 {
177 if (nKeyword == RTFKeyword::CELL)
179
181 if (m_bNeedPap)
182 {
183 // There were no runs in the cell, so we need to send paragraph and character properties here.
184 auto pPValue = new RTFValue(m_aStates.top().getParagraphAttributes(),
186 bufferProperties(m_aTableBufferStack.back(), pPValue, nullptr);
187 auto pCValue = new RTFValue(m_aStates.top().getCharacterAttributes(),
189 bufferProperties(m_aTableBufferStack.back(), pCValue, nullptr);
190 }
191
192 RTFValue::Pointer_t pValue;
193 m_aTableBufferStack.back().emplace_back(Buf_t(BUFFER_CELLEND, pValue, nullptr));
194 m_bNeedPap = true;
195 }
196 break;
198 {
199 tools::SvRef<TableRowBuffer> const pBuffer(
202 prepareProperties(m_aStates.top(), pBuffer->GetParaProperties(),
203 pBuffer->GetFrameProperties(), pBuffer->GetRowProperties(),
205
206 if (m_aTableBufferStack.size() == 1 || !m_aStates.top().getCurrentBuffer())
207 {
208 throw io::WrongFormatException("mismatch between \\itap and number of \\nestrow",
209 nullptr);
210 }
211 assert(m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back());
212 // note: there may be several states pointing to table buffer!
213 for (std::size_t i = 0; i < m_aStates.size(); ++i)
214 {
215 if (m_aStates[i].getCurrentBuffer() == &m_aTableBufferStack.back())
216 {
217 m_aStates[i].setCurrentBuffer(
219 }
220 }
221 m_aTableBufferStack.pop_back();
222 m_aTableBufferStack.back().emplace_back(
224
227 m_nNestedCells = 0;
228 m_bNeedPap = true;
229 }
230 break;
231 case RTFKeyword::ROW:
232 {
233 m_bAfterCellBeforeRow = false;
235 {
236 // Add fake cellx / cell, RTF equivalent of
237 // OOXMLFastContextHandlerTextTableRow::handleGridAfter().
238 auto pXValue = new RTFValue(m_aStates.top().getTableRowWidthAfter());
239 m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue,
242
243 // Adjust total width, which is done in the \cellx handler for normal cells.
245
247 }
248
249 bool bRestored = false;
250 // Ending a row, but no cells defined?
251 // See if there was an invalid table row reset, so we can restore cell infos to help invalid documents.
253 {
255 bRestored = true;
256 }
257
258 // 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
259 const int MINLAY = 23; // sw/inc/swtypes.hxx, minimal possible size of frames.
261 {
262 auto pXValueLast = m_aStates.top().getTableRowSprms().find(
263 NS_ooxml::LN_CT_TblGridBase_gridCol, false);
264 const int nXValueLast = pXValueLast ? pXValueLast->getInt() : 0;
265 auto pXValue = new RTFValue(nXValueLast + m_nCellxMax - m_nTopLevelCurrentCellX);
266 m_aStates.top().getTableRowSprms().eraseLast(NS_ooxml::LN_CT_TblGridBase_gridCol);
267 m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue,
270 }
271
273 {
274 // Make a backup before we start popping elements
278 }
279 else
280 {
281 // No table definition? Then inherit from the previous row
285 }
286
287 while (m_aTableBufferStack.size() > 1)
288 {
289 SAL_WARN("writerfilter.rtf", "dropping extra table buffer");
290 // note: there may be several states pointing to table buffer!
291 for (std::size_t i = 0; i < m_aStates.size(); ++i)
292 {
293 if (m_aStates[i].getCurrentBuffer() == &m_aTableBufferStack.back())
294 {
295 m_aStates[i].setCurrentBuffer(&m_aTableBufferStack.front());
296 }
297 }
298 m_aTableBufferStack.pop_back();
299 }
300
303
304 // The scope of the table cell defaults is one row.
308
312 prepareProperties(m_aStates.top(), paraProperties, frameProperties, rowProperties,
314 sendProperties(paraProperties, frameProperties, rowProperties);
315
316 m_bNeedPap = true;
317 m_bNeedFinalPar = true;
318 m_aTableBufferStack.back().clear();
320
321 if (bRestored)
322 // We restored cell definitions, clear these now.
323 // This is necessary, as later cell definitions want to overwrite the restored ones.
325 }
326 break;
328 {
329 bool bColumns = false; // If we have multiple columns
331 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_cols);
332 if (pCols)
333 {
334 RTFValue::Pointer_t pNum = pCols->getAttributes().find(NS_ooxml::LN_CT_Columns_num);
335 if (pNum && pNum->getInt() > 1)
336 bColumns = true;
337 }
339 if (bColumns)
340 {
341 sal_uInt8 const sBreak[] = { 0xe };
343 Mapper().text(sBreak, 1);
345 }
346 else
348 }
349 break;
351 {
353 // Stop buffering, there will be no custom mark for this footnote or endnote.
354 m_aStates.top().setCurrentBuffer(nullptr);
355 break;
356 }
357 case RTFKeyword::PAGE:
358 {
359 // Ignore page breaks inside tables.
361 break;
362
363 // If we're inside a continuous section, we should send a section break, not a page one.
365 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type);
366 // Unless we're on a title page.
367 RTFValue::Pointer_t pTitlePg
368 = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_titlePg);
369 if (((pBreak
370 && pBreak->getInt()
371 == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous))
373 && !(pTitlePg && pTitlePg->getInt()))
374 {
375 if (m_bWasInFrame)
376 {
378 m_bWasInFrame = false;
379 }
380 sectBreak();
381 // note: this will not affect the following section break
382 // but the one just pushed
384 if (m_bNeedPar)
387 // arrange to clean up the synthetic RTFKeyword::SBKPAGE
389 }
390 else
391 {
392 bool bFirstRun = m_bFirstRun;
394 if (bFirstRun || m_bNeedCr)
395 {
396 // Only send the paragraph properties early if we'll create a new paragraph in a
397 // bit anyway.
398 checkNeedPap();
399 }
400 sal_uInt8 const sBreak[] = { 0xc };
401 Mapper().text(sBreak, 1);
402 if (bFirstRun || m_bNeedCr)
403 {
404 // If we don't have content in the document yet (so the break-before can't move
405 // to a second layout page) or we already have characters sent (so the paragraph
406 // properties are already finalized), then continue inserting a fake paragraph.
407 if (!m_bNeedPap)
408 {
409 parBreak();
410 m_bNeedPap = true;
411 }
412 }
413 m_bNeedCr = true;
414 }
415 }
416 break;
418 {
419 OUString aStr("PAGE");
421 text(aStr);
422 singleChar(cFieldSep, true);
424 }
425 break;
427 {
428 static const sal_Unicode uFtnEdnSep = 0x3;
429 Mapper().utext(reinterpret_cast<const sal_uInt8*>(&uFtnEdnSep), 1);
430 }
431 break;
432 default:
433 {
434 SAL_INFO("writerfilter.rtf",
435 "TODO handle symbol '" << keywordToString(nKeyword) << "'");
436 aSkip.setParsed(false);
437 }
438 break;
439 }
440 return RTFError::OK;
441}
442
443} // namespace writerfilter::rtftok
444
445/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const sal_Unicode uFtnEdnSep
virtual void endCharacterGroup()=0
Receives end mark for group with the same character properties.
virtual void props(writerfilter::Reference< Properties >::Pointer_t ref)=0
Receives properties of the current run of text.
virtual void text(const sal_uInt8 *data, size_t len)=0
Receives 8-bit per character text.
virtual void startCharacterGroup()=0
Receives start mark for group with the same character properties.
virtual void utext(const sal_uInt8 *data, size_t len)=0
Receives 16-bit per character text.
int m_nInheritingCells
backup of top-level props, to support inheriting cell props
void replayRowBuffer(RTFBuffer_t &rBuffer, ::std::deque< RTFSprms > &rCellsSprms, ::std::deque< RTFSprms > &rCellsAttributes, int nCells)
int m_nNestedCurrentCellX
Current cellx value (nested table)
void setNeedSect(bool bNeedSect)
If we need a final section break at the end of the document.
std::deque< RTFBuffer_t > m_aTableBufferStack
Buffered table cells, till cell definitions are not reached.
bool m_bIgnoreNextContSectBreak
If the next continuous section break should be ignored.
std::deque< RTFSprms > m_aTopLevelTableCellsAttributes
std::deque< RTFSprms > m_aTableInheritingCellsSprms
bool m_bNeedFinalPar
If set, an empty paragraph will be added at the end of the document.
RTFKeyword m_nResetBreakOnSectBreak
clean up a synthetic page break, see RTF_PAGE if inactive value is -1, otherwise the RTF_SKB* to rest...
RTFError dispatchSymbol(RTFKeyword nKeyword) override
bool m_bHadSect
The document has multiple sections.
void singleChar(sal_uInt8 nValue, bool bRunProps=false)
int m_nTopLevelCurrentCellX
Current cellx value (top-level table)
std::deque< RTFSprms > m_aNestedTableCellsAttributes
bool m_bFirstRun
to start initial paragraph / section after font/style tables
std::deque< RTFSprms > m_aTopLevelTableCellsSprms
void checkUnicode(bool bUnicode, bool bHex)
If we have some unicode or hex characters to send.
RTFBuffer_t m_aSuperBuffer
Buffered superscript, till footnote is reached (or not).
std::deque< RTFSprms > m_aTableInheritingCellsAttributes
std::deque< RTFSprms > m_aNestedTableCellsSprms
int m_nNestedCells
cell props buffer for nested tables, reset by \nestrow the \nesttableprops is a destination and must ...
void bufferProperties(RTFBuffer_t &rBuffer, const RTFValue::Pointer_t &pValue, const tools::SvRef< TableRowBuffer > &pTableProperties, Id nStyleType=0)
Buffers properties to be sent later.
void sendProperties(writerfilter::Reference< Properties >::Pointer_t const &pParagraphProperties, writerfilter::Reference< Properties >::Pointer_t const &pFrameProperties, writerfilter::Reference< Properties >::Pointer_t const &pTableRowProperties)
Send the passed properties to dmapper.
bool m_bNeedCr
If we need to emit a CR at the end of substream.
int m_nTopLevelCells
cell props buffer for top-level table, reset by \row
void prepareProperties(RTFParserState &rState, writerfilter::Reference< Properties >::Pointer_t &o_rpParagraphProperties, writerfilter::Reference< Properties >::Pointer_t &o_rpFrameProperties, writerfilter::Reference< Properties >::Pointer_t &o_rpTableRowProperties, int nCells, int nCurrentCellX)
RTFParserState m_aDefaultState
Read by RTF_PARD.
void checkFirstRun()
If this is the first run of the document, starts the initial paragraph.
RTFError dispatchFlag(RTFKeyword nKeyword) override
bool m_bAfterCellBeforeRow
Are we after a \cell, but before a \row?
bool m_bNeedPap
If paragraph properties should be emitted on next run.
bool m_bWasInFrame
If aFrame.inFrame() was true in the previous state.
bool m_bNeedSect
If a section break is needed before the end of the doc (false right after a section break).
int m_nCellxMax
Max width of the rows in the current table.
bool inFrame() const
If we got tokens indicating we're in a frame.
void setCurrentBuffer(RTFBuffer_t *pCurrentBuffer)
void setTableRowWidthAfter(int nTableRowWidthAfter)
void setInternalState(RTFInternalState nInternalState)
Sends RTFSprm instances to DomainMapper.
Skips a destination after a not parsed control word if it was prefixed with *.
A list of RTFSprm with a copy constructor that performs a deep copy.
Definition: rtfsprm.hxx:39
RTFValue::Pointer_t find(Id nKeyword, bool bFirst=true, bool bForWrite=false)
Definition: rtfsprm.cxx:74
void set(Id nKeyword, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite=RTFOverwrite::YES)
Does the same as ->push_back(), except that it can overwrite or ignore existing entries.
Definition: rtfsprm.cxx:98
void eraseLast(Id nKeyword)
Definition: rtfsprm.cxx:149
Value of an RTF keyword.
Definition: rtfvalue.hxx:33
#define SVT_SOFT_HYPHEN
#define SVT_HARD_HYPHEN
#define SVT_HARD_SPACE
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
aStr
int i
std::tuple< RTFBufferTypes, RTFValue::Pointer_t, tools::SvRef< TableRowBuffer > > Buf_t
A buffer storing dmapper calls.
@ NO_APPEND
No, always append the value to the end of the list.
const char * keywordToString(RTFKeyword nKeyword)
const sal_uInt8 cFieldSep
const sal_uInt8 cFieldStart
const sal_uInt8 cFieldEnd
#define MINLAY
unsigned char sal_uInt8
sal_uInt16 sal_Unicode