1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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
28 RTFError
RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword
)
31 if (nKeyword
!= RTF_HEXCHAR
)
32 checkUnicode(/*bUnicode =*/true, /*bHex =*/true);
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'
85 OUString
aStr(OStringToOUString(OString(cCh
), RTL_TEXTENCODING_MS_1252
));
94 m_bSkipUnknown
= true;
95 aSkip
.setReset(false);
101 if (m_aStates
.top().getDestination() == Destination::FOOTNOTESEPARATOR
)
102 break; // just ignore it - only thing we read in here is CHFTNSEP
104 bool bNeedPap
= m_bNeedPap
;
108 if (!m_aStates
.top().getCurrentBuffer())
111 // Not in table? Reset max width.
114 // Was in table, but not anymore -> tblEnd.
115 RTFSprms aAttributes
;
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
);
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
131 if (!m_aStates
.top().getFrame().inFrame())
133 m_bNeedFinalPar
= false;
139 if (m_bIgnoreNextContSectBreak
)
140 m_bIgnoreNextContSectBreak
= false;
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
156 OUString
aStr(SVT_HARD_SPACE
);
162 OUString
aStr(SVT_HARD_HYPHEN
);
168 OUString
aStr(SVT_SOFT_HYPHEN
);
173 m_aStates
.top().setInternalState(RTFInternalState::HEX
);
178 if (nKeyword
== RTF_CELL
)
179 m_bAfterCellBeforeRow
= true;
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));
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",
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();
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();
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
;
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
);
318 m_bNeedFinalPar
= true;
319 m_aTableBufferStack
.back().clear();
320 m_nTopLevelCells
= 0;
323 // We restored cell definitions, clear these now.
324 // This is necessary, as later cell definitions want to overwrite the restored ones.
325 resetTableRowProperties();
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
);
335 RTFValue::Pointer_t pNum
= pCols
->getAttributes().find(NS_ooxml::LN_CT_Columns_num
);
336 if (pNum
.get() && pNum
->getInt() > 1)
342 sal_uInt8
const sBreak
[] = { 0xe };
343 Mapper().startCharacterGroup();
344 Mapper().text(sBreak
, 1);
345 Mapper().endCharacterGroup();
348 dispatchSymbol(RTF_PAGE
);
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);
360 // Ignore page breaks inside tables.
361 if (m_aStates
.top().getCurrentBuffer() == &m_aTableBufferStack
.back())
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
);
372 == static_cast<sal_Int32
>(NS_ooxml::LN_Value_ST_SectionMark_continuous
))
373 || m_nResetBreakOnSectBreak
== RTF_SBKNONE
)
374 && !(pTitlePg
.get() && pTitlePg
->getInt()))
378 dispatchSymbol(RTF_PAR
);
379 m_bWasInFrame
= false;
382 // note: this will not affect the following section break
383 // but the one just pushed
384 dispatchFlag(RTF_SBKPAGE
);
386 dispatchSymbol(RTF_PAR
);
387 m_bIgnoreNextContSectBreak
= true;
388 // arrange to clean up the synthetic RTF_SBKPAGE
389 m_nResetBreakOnSectBreak
= RTF_SBKNONE
;
395 sal_uInt8
const sBreak
[] = { 0xc };
396 Mapper().text(sBreak
, 1);
408 OUString
aStr("PAGE");
409 singleChar(cFieldStart
);
411 singleChar(cFieldSep
, true);
412 singleChar(cFieldEnd
);
417 static const sal_Unicode uFtnEdnSep
= 0x3;
418 Mapper().utext(reinterpret_cast<const sal_uInt8
*>(&uFtnEdnSep
), 1);
423 SAL_INFO("writerfilter.rtf",
424 "TODO handle symbol '" << keywordToString(nKeyword
) << "'");
425 aSkip
.setParsed(false);
432 } // namespace rtftok
433 } // namespace writerfilter
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */