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 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <memory>
21 #include <hintids.hxx>
22 #include <comphelper/flagguard.hxx>
23 #include <vcl/svapp.hxx>
24 #include <editeng/boxitem.hxx>
25 #include <editeng/brushitem.hxx>
26 #include <editeng/adjustitem.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/ulspitem.hxx>
29 #include <editeng/lrspitem.hxx>
30 #include <editeng/formatbreakitem.hxx>
31 #include <editeng/spltitem.hxx>
32 #include <unotools/configmgr.hxx>
33 #include <svtools/htmltokn.h>
34 #include <svtools/htmlkywd.hxx>
35 #include <svl/urihelper.hxx>
36 #include <svl/listener.hxx>
37 #include <sal/log.hxx>
38
39 #include <dcontact.hxx>
40 #include <fmtornt.hxx>
41 #include <frmfmt.hxx>
42 #include <fmtfsize.hxx>
43 #include <fmtsrnd.hxx>
44 #include <fmtpdsc.hxx>
45 #include <fmtcntnt.hxx>
46 #include <fmtanchr.hxx>
47 #include <fmtlsplt.hxx>
48 #include <frmatr.hxx>
49 #include <pam.hxx>
50 #include <doc.hxx>
51 #include <IDocumentLayoutAccess.hxx>
52 #include <IDocumentMarkAccess.hxx>
53 #include <ndtxt.hxx>
54 #include <shellio.hxx>
55 #include <poolfmt.hxx>
56 #include <swtable.hxx>
57 #include <cellatr.hxx>
58 #include <htmltbl.hxx>
59 #include <swtblfmt.hxx>
60 #include "htmlnum.hxx"
61 #include "swhtml.hxx"
62 #include "swcss1.hxx"
63 #include <numrule.hxx>
64 #include <txtftn.hxx>
65 #include <itabenum.hxx>
66 #include <tblafmt.hxx>
67 #include <SwStyleNameMapper.hxx>
68
69 #define NETSCAPE_DFLT_BORDER 1
70 #define NETSCAPE_DFLT_CELLSPACING 2
71
72 using ::editeng::SvxBorderLine;
73 using namespace ::com::sun::star;
74
75 static HTMLOptionEnum<sal_Int16> const aHTMLTableVAlignTable[] =
76 {
77 { OOO_STRING_SVTOOLS_HTML_VA_top, text::VertOrientation::NONE },
78 { OOO_STRING_SVTOOLS_HTML_VA_middle, text::VertOrientation::CENTER },
79 { OOO_STRING_SVTOOLS_HTML_VA_bottom, text::VertOrientation::BOTTOM },
80 { nullptr, 0 }
81 };
82
83 // table tags options
84
85 struct HTMLTableOptions
86 {
87 sal_uInt16 nCols;
88 sal_uInt16 nWidth;
89 sal_uInt16 nHeight;
90 sal_uInt16 nCellPadding;
91 sal_uInt16 nCellSpacing;
92 sal_uInt16 nBorder;
93 sal_uInt16 nHSpace;
94 sal_uInt16 nVSpace;
95
96 SvxAdjust eAdjust;
97 sal_Int16 eVertOri;
98 HTMLTableFrame eFrame;
99 HTMLTableRules eRules;
100
101 bool bPrcWidth : 1;
102 bool bTableAdjust : 1;
103 bool bBGColor : 1;
104
105 Color aBorderColor;
106 Color aBGColor;
107
108 OUString aBGImage, aStyle, aId, aClass, aDir;
109
110 HTMLTableOptions( const HTMLOptions& rOptions, SvxAdjust eParentAdjust );
111 };
112
113 class HTMLTableContext
114 {
115 SwHTMLNumRuleInfo aNumRuleInfo; // Numbering valid before the table
116
117 SwTableNode *pTableNd; // table node
118 SwFrameFormat *pFrameFormat; // the Fly frame::Frame, containing the table
119 std::unique_ptr<SwPosition> pPos; // position behind the table
120
121 size_t const nContextStAttrMin;
122 size_t const nContextStMin;
123
124 bool bRestartPRE : 1;
125 bool bRestartXMP : 1;
126 bool bRestartListing : 1;
127
128 HTMLTableContext(const HTMLTableContext&) = delete;
129 HTMLTableContext& operator=(const HTMLTableContext&) = delete;
130
131 public:
132
133 std::shared_ptr<HTMLAttrTable> xAttrTab; // attributes
134
HTMLTableContext(SwPosition * pPs,size_t nCntxtStMin,size_t nCntxtStAttrMin)135 HTMLTableContext( SwPosition *pPs, size_t nCntxtStMin,
136 size_t nCntxtStAttrMin ) :
137 pTableNd( nullptr ),
138 pFrameFormat( nullptr ),
139 pPos( pPs ),
140 nContextStAttrMin( nCntxtStAttrMin ),
141 nContextStMin( nCntxtStMin ),
142 bRestartPRE( false ),
143 bRestartXMP( false ),
144 bRestartListing( false ),
145 xAttrTab(new HTMLAttrTable)
146 {
147 memset(xAttrTab.get(), 0, sizeof(HTMLAttrTable));
148 }
149
SetNumInfo(const SwHTMLNumRuleInfo & rInf)150 void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { aNumRuleInfo.Set(rInf); }
GetNumInfo() const151 const SwHTMLNumRuleInfo& GetNumInfo() const { return aNumRuleInfo; };
152
153 void SavePREListingXMP( SwHTMLParser& rParser );
154 void RestorePREListingXMP( SwHTMLParser& rParser );
155
GetPos() const156 SwPosition *GetPos() const { return pPos.get(); }
157
SetTableNode(SwTableNode * pNd)158 void SetTableNode( SwTableNode *pNd ) { pTableNd = pNd; }
GetTableNode() const159 SwTableNode *GetTableNode() const { return pTableNd; }
160
SetFrameFormat(SwFrameFormat * pFormat)161 void SetFrameFormat( SwFrameFormat *pFormat ) { pFrameFormat = pFormat; }
GetFrameFormat() const162 SwFrameFormat *GetFrameFormat() const { return pFrameFormat; }
163
GetContextStMin() const164 size_t GetContextStMin() const { return nContextStMin; }
GetContextStAttrMin() const165 size_t GetContextStAttrMin() const { return nContextStAttrMin; }
166 };
167
168 // Cell content is a linked list with SwStartNodes and
169 // HTMLTables.
170
171 class HTMLTableCnts
172 {
173 std::unique_ptr<HTMLTableCnts> m_pNext; // next content
174
175 // Only one of the next two pointers must be set!
176 const SwStartNode *m_pStartNode; // a paragraph
177 std::shared_ptr<HTMLTable> m_xTable; // a table
178
179 std::shared_ptr<SwHTMLTableLayoutCnts> m_xLayoutInfo;
180
181 bool m_bNoBreak;
182
183 void InitCtor();
184
185 public:
186
187 explicit HTMLTableCnts(const SwStartNode* pStNd);
188 explicit HTMLTableCnts(const std::shared_ptr<HTMLTable>& rTab);
189
190 ~HTMLTableCnts(); // only allowed in ~HTMLTableCell
191
192 // Determine SwStartNode and HTMLTable respectively
GetStartNode() const193 const SwStartNode *GetStartNode() const { return m_pStartNode; }
GetTable() const194 const std::shared_ptr<HTMLTable>& GetTable() const { return m_xTable; }
GetTable()195 std::shared_ptr<HTMLTable>& GetTable() { return m_xTable; }
196
197 // Add a new node at the end of the list
198 void Add( std::unique_ptr<HTMLTableCnts> pNewCnts );
199
200 // Determine next node
Next() const201 const HTMLTableCnts *Next() const { return m_pNext.get(); }
Next()202 HTMLTableCnts *Next() { return m_pNext.get(); }
203
204 inline void SetTableBox( SwTableBox *pBox );
205
SetNoBreak()206 void SetNoBreak() { m_bNoBreak = true; }
207
208 const std::shared_ptr<SwHTMLTableLayoutCnts>& CreateLayoutInfo();
209 };
210
211 // Cell of a HTML table
212 class HTMLTableCell
213 {
214 std::shared_ptr<HTMLTableCnts> m_xContents; // cell content
215 std::shared_ptr<SvxBrushItem> m_xBGBrush; // cell background
216 std::shared_ptr<SvxBoxItem> m_xBoxItem;
217
218 double m_nValue;
219 sal_uInt32 m_nNumFormat;
220 sal_uInt16 m_nRowSpan; // cell ROWSPAN
221 sal_uInt16 m_nColSpan; // cell COLSPAN
222 sal_uInt16 m_nWidth; // cell WIDTH
223 sal_Int16 m_eVertOrient; // vertical alignment of the cell
224 bool m_bProtected : 1; // cell must not filled
225 bool m_bRelWidth : 1; // nWidth is given in %
226 bool m_bHasNumFormat : 1;
227 bool m_bHasValue : 1;
228 bool m_bNoWrap : 1;
229 bool mbCovered : 1;
230
231 public:
232
233 HTMLTableCell(); // new cells always empty
234
235 // Fill a not empty cell
236 void Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan,
237 sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrush,
238 std::shared_ptr<SvxBoxItem> const& rBoxItem,
239 bool bHasNumFormat, sal_uInt32 nNumFormat,
240 bool bHasValue, double nValue, bool bNoWrap, bool bCovered );
241
242 // Protect an empty 1x1 cell
243 void SetProtected();
244
245 // Set/Get cell content
SetContents(std::shared_ptr<HTMLTableCnts> const & rCnts)246 void SetContents(std::shared_ptr<HTMLTableCnts> const& rCnts) { m_xContents = rCnts; }
GetContents() const247 const std::shared_ptr<HTMLTableCnts>& GetContents() const { return m_xContents; }
248
249 // Set/Get cell ROWSPAN/COLSPAN
SetRowSpan(sal_uInt16 nRSpan)250 void SetRowSpan( sal_uInt16 nRSpan ) { m_nRowSpan = nRSpan; }
GetRowSpan() const251 sal_uInt16 GetRowSpan() const { return m_nRowSpan; }
252
SetColSpan(sal_uInt16 nCSpan)253 void SetColSpan( sal_uInt16 nCSpan ) { m_nColSpan = nCSpan; }
GetColSpan() const254 sal_uInt16 GetColSpan() const { return m_nColSpan; }
255
256 inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth );
257
GetBGBrush() const258 const std::shared_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
GetBoxItem() const259 const std::shared_ptr<SvxBoxItem>& GetBoxItem() const { return m_xBoxItem; }
260
261 inline bool GetNumFormat( sal_uInt32& rNumFormat ) const;
262 inline bool GetValue( double& rValue ) const;
263
GetVertOri() const264 sal_Int16 GetVertOri() const { return m_eVertOrient; }
265
266 // Is the cell filled or protected ?
IsUsed() const267 bool IsUsed() const { return m_xContents || m_bProtected; }
268
269 std::unique_ptr<SwHTMLTableLayoutCell> CreateLayoutInfo();
270
IsCovered() const271 bool IsCovered() const { return mbCovered; }
272 };
273
274 // Row of a HTML table
275 typedef std::vector<HTMLTableCell> HTMLTableCells;
276
277 class HTMLTableRow
278 {
279 HTMLTableCells m_aCells; ///< cells of the row
280 std::unique_ptr<SvxBrushItem> xBGBrush; // background of cell from STYLE
281
282 SvxAdjust eAdjust;
283 sal_uInt16 nHeight; // options of <TR>/<TD>
284 sal_uInt16 nEmptyRows; // number of empty rows are following
285 sal_Int16 eVertOri;
286 bool bIsEndOfGroup : 1;
287 bool bBottomBorder : 1; // Is there a line after the row?
288
289 public:
290
291 explicit HTMLTableRow( sal_uInt16 nCells ); // cells of the row are empty
292
SetBottomBorder(bool bIn)293 void SetBottomBorder(bool bIn) { bBottomBorder = bIn; }
GetBottomBorder() const294 bool GetBottomBorder() const { return bBottomBorder; }
295
296 inline void SetHeight( sal_uInt16 nHeight );
GetHeight() const297 sal_uInt16 GetHeight() const { return nHeight; }
298
299 const HTMLTableCell& GetCell(sal_uInt16 nCell) const;
GetCell(sal_uInt16 nCell)300 HTMLTableCell& GetCell(sal_uInt16 nCell)
301 {
302 return const_cast<HTMLTableCell&>(const_cast<const HTMLTableRow&>(*this).GetCell(nCell));
303 }
304
SetAdjust(SvxAdjust eAdj)305 void SetAdjust( SvxAdjust eAdj ) { eAdjust = eAdj; }
GetAdjust() const306 SvxAdjust GetAdjust() const { return eAdjust; }
307
SetVertOri(sal_Int16 eV)308 void SetVertOri( sal_Int16 eV) { eVertOri = eV; }
GetVertOri() const309 sal_Int16 GetVertOri() const { return eVertOri; }
310
SetBGBrush(std::unique_ptr<SvxBrushItem> & rBrush)311 void SetBGBrush(std::unique_ptr<SvxBrushItem>& rBrush ) { xBGBrush = std::move(rBrush); }
GetBGBrush() const312 const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return xBGBrush; }
313
SetEndOfGroup()314 void SetEndOfGroup() { bIsEndOfGroup = true; }
IsEndOfGroup() const315 bool IsEndOfGroup() const { return bIsEndOfGroup; }
316
IncEmptyRows()317 void IncEmptyRows() { nEmptyRows++; }
GetEmptyRows() const318 sal_uInt16 GetEmptyRows() const { return nEmptyRows; }
319
320 // Expand row by adding empty cells
321 void Expand( sal_uInt16 nCells, bool bOneCell=false );
322
323 // Shrink row by deleting empty cells
324 void Shrink( sal_uInt16 nCells );
325 };
326
327 // Column of a HTML table
328 class HTMLTableColumn
329 {
330 bool bIsEndOfGroup;
331
332 sal_uInt16 nWidth; // options of <COL>
333 bool bRelWidth;
334
335 SvxAdjust eAdjust;
336 sal_Int16 eVertOri;
337
338 SwFrameFormat *aFrameFormats[6];
339
340 static inline sal_uInt16 GetFrameFormatIdx( bool bBorderLine,
341 sal_Int16 eVertOri );
342
343 public:
344
345 bool bLeftBorder; // is there a line before the column
346
347 HTMLTableColumn();
348
349 inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth);
350
SetAdjust(SvxAdjust eAdj)351 void SetAdjust( SvxAdjust eAdj ) { eAdjust = eAdj; }
GetAdjust() const352 SvxAdjust GetAdjust() const { return eAdjust; }
353
SetVertOri(sal_Int16 eV)354 void SetVertOri( sal_Int16 eV) { eVertOri = eV; }
GetVertOri() const355 sal_Int16 GetVertOri() const { return eVertOri; }
356
SetEndOfGroup()357 void SetEndOfGroup() { bIsEndOfGroup = true; }
IsEndOfGroup() const358 bool IsEndOfGroup() const { return bIsEndOfGroup; }
359
360 inline void SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
361 sal_Int16 eVertOri );
362 inline SwFrameFormat *GetFrameFormat( bool bBorderLine,
363 sal_Int16 eVertOri ) const;
364
365 std::unique_ptr<SwHTMLTableLayoutColumn> CreateLayoutInfo();
366 };
367
368 // HTML table
369 typedef std::vector<HTMLTableRow> HTMLTableRows;
370
371 typedef std::vector<HTMLTableColumn> HTMLTableColumns;
372
373 typedef std::vector<SdrObject *> SdrObjects;
374
375 class HTMLTable
376 {
377 OUString m_aId;
378 OUString m_aStyle;
379 OUString m_aClass;
380 OUString m_aDir;
381
382 std::unique_ptr<SdrObjects> m_pResizeDrawObjects;// SDR objects
383 std::unique_ptr<std::vector<sal_uInt16>> m_pDrawObjectPrcWidths; // column of draw object and its rel. width
384
385 HTMLTableRows m_aRows; ///< table rows
386 HTMLTableColumns m_aColumns; ///< table columns
387
388 sal_uInt16 m_nRows; // number of rows
389 sal_uInt16 m_nCols; // number of columns
390 sal_uInt16 m_nFilledColumns; // number of filled columns
391
392 sal_uInt16 m_nCurrentRow; // current Row
393 sal_uInt16 m_nCurrentColumn; // current Column
394
395 sal_uInt16 m_nLeftMargin; // Space to the left margin (from paragraph edge)
396 sal_uInt16 m_nRightMargin; // Space to the right margin (from paragraph edge)
397
398 sal_uInt16 m_nCellPadding; // Space from border to Text
399 sal_uInt16 m_nCellSpacing; // Space between two cells
400 sal_uInt16 m_nHSpace;
401 sal_uInt16 m_nVSpace;
402
403 sal_uInt16 m_nBoxes; // number of boxes in the table
404
405 const SwStartNode *m_pPrevStartNode; // the Table-Node or the Start-Node of the section before
406 const SwTable *m_pSwTable; // SW-Table (only on Top-Level)
407 public:
408 std::unique_ptr<SwTableBox> m_xBox1; // TableBox, generated when the Top-Level-Table was build
409 private:
410 SwTableBoxFormat *m_pBoxFormat; // frame::Frame-Format from SwTableBox
411 SwTableLineFormat *m_pLineFormat; // frame::Frame-Format from SwTableLine
412 SwTableLineFormat *m_pLineFrameFormatNoHeight;
413 std::unique_ptr<SvxBrushItem> m_xBackgroundBrush; // background of the table
414 std::unique_ptr<SvxBrushItem> m_xInheritedBackgroundBrush; // "inherited" background of the table
415 const SwStartNode *m_pCaptionStartNode; // Start-Node of the table-caption
416 //lines for the border
417 SvxBorderLine m_aTopBorderLine;
418 SvxBorderLine m_aBottomBorderLine;
419 SvxBorderLine m_aLeftBorderLine;
420 SvxBorderLine m_aRightBorderLine;
421 SvxBorderLine m_aBorderLine;
422 SvxBorderLine m_aInheritedLeftBorderLine;
423 SvxBorderLine m_aInheritedRightBorderLine;
424 bool m_bTopBorder; // is there a line on the top of the table
425 bool m_bRightBorder; // is there a line on the top right of the table
426 bool m_bTopAllowed; // is it allowed to set the border?
427 bool m_bRightAllowed;
428 bool m_bFillerTopBorder; // gets the left/right filler-cell a border on the
429 bool m_bFillerBottomBorder; // top or in the bottom
430 bool m_bInheritedLeftBorder;
431 bool m_bInheritedRightBorder;
432 bool m_bBordersSet; // the border is set already
433 bool m_bForceFrame;
434 bool const m_bTableAdjustOfTag; // comes nTableAdjust from <TABLE>?
435 sal_uInt32 m_nHeadlineRepeat; // repeating rows
436 bool const m_bIsParentHead;
437 bool m_bHasParentSection;
438 bool const m_bHasToFly;
439 bool const m_bFixedCols;
440 bool m_bColSpec; // where there COL(GROUP)-elements?
441 bool const m_bPrcWidth; // width is declared in %
442
443 SwHTMLParser *m_pParser; // the current parser
444 std::unique_ptr<HTMLTableCnts> m_xParentContents;
445
446 std::unique_ptr<HTMLTableContext> m_pContext; // the context of the table
447
448 std::shared_ptr<SwHTMLTableLayout> m_xLayoutInfo;
449
450 // the following parameters are from the <TABLE>-Tag
451 sal_uInt16 const m_nWidth; // width of the table
452 sal_uInt16 m_nHeight; // absolute height of the table
453 SvxAdjust const m_eTableAdjust; // drawing::Alignment of the table
454 sal_Int16 const m_eVertOrientation; // Default vertical direction of the cells
455 sal_uInt16 m_nBorder; // width of the external border
456 HTMLTableFrame const m_eFrame; // frame around the table
457 HTMLTableRules const m_eRules; // frame in the table
458 bool m_bTopCaption; // Caption of the table
459
460 void InitCtor(const HTMLTableOptions& rOptions);
461
462 // Correction of the Row-Spans for all cells above the chosen cell and the cell itself for the indicated content. The chosen cell gets the Row-Span 1
463 void FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, const HTMLTableCnts *pCnts );
464
465 // Protects the chosen cell and the cells among
466 void ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan );
467
468 // Looking for the SwStartNodes of the box ahead
469 // If nRow==nCell==USHRT_MAX, return the last Start-Node of the table.
470 const SwStartNode* GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCell ) const;
471
472 sal_uInt16 GetTopCellSpace( sal_uInt16 nRow ) const;
473 sal_uInt16 GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const;
474
475 // Conforming of the frame::Frame-Format of the box
476 void FixFrameFormat( SwTableBox *pBox, sal_uInt16 nRow, sal_uInt16 nCol,
477 sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
478 bool bFirstPara=true, bool bLastPara=true ) const;
479
480 // Create a table with the content (lines/boxes)
481 void MakeTable_( SwTableBox *pUpper );
482
483 // Generate a new SwTableBox, which contains a SwStartNode
484 SwTableBox *NewTableBox( const SwStartNode *pStNd,
485 SwTableLine *pUpper ) const;
486
487 // Generate a SwTableLine from the cells of the rectangle
488 // (nTopRow/nLeftCol) inclusive to (nBottomRow/nRightRow) exclusive
489 SwTableLine *MakeTableLine( SwTableBox *pUpper,
490 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
491 sal_uInt16 nBottomRow, sal_uInt16 nRightCol );
492
493 // Generate a SwTableBox from the content of the cell
494 SwTableBox *MakeTableBox( SwTableLine *pUpper,
495 HTMLTableCnts *pCnts,
496 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
497 sal_uInt16 nBootomRow, sal_uInt16 nRightCol );
498
499 // Autolayout-Algorithm
500
501 // Setting the border with the help of guidelines of the Parent-Table
502 void InheritBorders( const HTMLTable *pParent,
503 sal_uInt16 nRow, sal_uInt16 nCol,
504 sal_uInt16 nRowSpan,
505 bool bFirstPara, bool bLastPara );
506
507 // Inherit the left and the right border of the surrounding table
508 void InheritVertBorders( const HTMLTable *pParent,
509 sal_uInt16 nCol, sal_uInt16 nColSpan );
510
511 // Set the border with the help of the information from the user
512 void SetBorders();
513
514 // is the border already set?
BordersSet() const515 bool BordersSet() const { return m_bBordersSet; }
516
GetBGBrush() const517 const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBackgroundBrush; }
GetInhBGBrush() const518 const std::unique_ptr<SvxBrushItem>& GetInhBGBrush() const { return m_xInheritedBackgroundBrush; }
519
520 sal_uInt16 GetBorderWidth( const SvxBorderLine& rBLine,
521 bool bWithDistance=false ) const;
522
523 public:
524
525 bool m_bFirstCell; // is there a cell created already?
526
527 HTMLTable(SwHTMLParser* pPars,
528 bool bParHead, bool bHasParentSec,
529 bool bHasToFly,
530 const HTMLTableOptions& rOptions);
531
532 ~HTMLTable();
533
534 // Identifying of a cell
535 const HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const;
GetCell(sal_uInt16 nRow,sal_uInt16 nCell)536 HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell)
537 {
538 return const_cast<HTMLTableCell&>(const_cast<const HTMLTable&>(*this).GetCell(nRow, nCell));
539 }
540
541 // set/determine caption
542 inline void SetCaption( const SwStartNode *pStNd, bool bTop );
GetCaptionStartNode() const543 const SwStartNode *GetCaptionStartNode() const { return m_pCaptionStartNode; }
IsTopCaption() const544 bool IsTopCaption() const { return m_bTopCaption; }
545
GetTableAdjust(bool bAny) const546 SvxAdjust GetTableAdjust( bool bAny ) const
547 {
548 return (m_bTableAdjustOfTag || bAny) ? m_eTableAdjust : SvxAdjust::End;
549 }
550
GetHSpace() const551 sal_uInt16 GetHSpace() const { return m_nHSpace; }
GetVSpace() const552 sal_uInt16 GetVSpace() const { return m_nVSpace; }
553
554 // get inherited drawing::Alignment of rows and column
555 SvxAdjust GetInheritedAdjust() const;
556 sal_Int16 GetInheritedVertOri() const;
557
558 // Insert a cell on the current position
559 void InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
560 sal_uInt16 nWidth, bool bRelWidth, sal_uInt16 nHeight,
561 sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrushItem,
562 std::shared_ptr<SvxBoxItem> const& rBoxItem,
563 bool bHasNumFormat, sal_uInt32 nNumFormat,
564 bool bHasValue, double nValue, bool bNoWrap );
565
566 // announce the start/end of a new row
567 void OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOri, std::unique_ptr<SvxBrushItem>& rBGBrush);
568 void CloseRow( bool bEmpty );
569
570 // announce the end of a new section
571 inline void CloseSection( bool bHead );
572
573 // announce the end of a column-group
574 inline void CloseColGroup( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
575 SvxAdjust eAdjust, sal_Int16 eVertOri );
576
577 // insert a new column
578 void InsertCol( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
579 SvxAdjust eAdjust, sal_Int16 eVertOri );
580
581 // End a table definition (needs to be called for every table)
582 void CloseTable();
583
584 // Construct a SwTable (including child tables)
585 void MakeTable( SwTableBox *pUpper, sal_uInt16 nAbsAvail,
586 sal_uInt16 nRelAvail=0, sal_uInt16 nAbsLeftSpace=0,
587 sal_uInt16 nAbsRightSpace=0, sal_uInt16 nInhAbsSpace=0 );
588
IsNewDoc() const589 bool IsNewDoc() const { return m_pParser->IsNewDoc(); }
590
SetHasParentSection(bool bSet)591 void SetHasParentSection( bool bSet ) { m_bHasParentSection = bSet; }
HasParentSection() const592 bool HasParentSection() const { return m_bHasParentSection; }
593
SetParentContents(std::unique_ptr<HTMLTableCnts> pCnts)594 void SetParentContents(std::unique_ptr<HTMLTableCnts> pCnts) { m_xParentContents = std::move(pCnts); }
GetParentContents()595 std::unique_ptr<HTMLTableCnts>& GetParentContents() { return m_xParentContents; }
596
597 void MakeParentContents();
598
GetIsParentHeader() const599 bool GetIsParentHeader() const { return m_bIsParentHead; }
600
HasToFly() const601 bool HasToFly() const { return m_bHasToFly; }
602
603 void SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt,
604 sal_uInt16 nLeft, sal_uInt16 nRight,
605 const SwTable *pSwTab=nullptr, bool bFrcFrame=false );
606
GetContext() const607 HTMLTableContext *GetContext() const { return m_pContext.get(); }
608
609 const std::shared_ptr<SwHTMLTableLayout>& CreateLayoutInfo();
610
HasColTags() const611 bool HasColTags() const { return m_bColSpec; }
612
IncGrfsThatResize()613 sal_uInt16 IncGrfsThatResize() { return m_pSwTable ? const_cast<SwTable *>(m_pSwTable)->IncGrfsThatResize() : 0; }
614
615 void RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPrcWidth );
616
GetSwTable() const617 const SwTable *GetSwTable() const { return m_pSwTable; }
618
SetBGBrush(const SvxBrushItem & rBrush)619 void SetBGBrush(const SvxBrushItem& rBrush) { m_xBackgroundBrush.reset(new SvxBrushItem(rBrush)); }
620
GetId() const621 const OUString& GetId() const { return m_aId; }
GetClass() const622 const OUString& GetClass() const { return m_aClass; }
GetStyle() const623 const OUString& GetStyle() const { return m_aStyle; }
GetDirection() const624 const OUString& GetDirection() const { return m_aDir; }
625
IncBoxCount()626 void IncBoxCount() { m_nBoxes++; }
IsOverflowing() const627 bool IsOverflowing() const { return m_nBoxes > 64000; }
628
629 bool PendingDrawObjectsInPaM(SwPaM& rPam) const;
630 };
631
InitCtor()632 void HTMLTableCnts::InitCtor()
633 {
634 m_pNext = nullptr;
635 m_xLayoutInfo.reset();
636 m_bNoBreak = false;
637 }
638
HTMLTableCnts(const SwStartNode * pStNd)639 HTMLTableCnts::HTMLTableCnts(const SwStartNode* pStNd)
640 : m_pStartNode(pStNd)
641 {
642 InitCtor();
643 }
644
HTMLTableCnts(const std::shared_ptr<HTMLTable> & rTab)645 HTMLTableCnts::HTMLTableCnts(const std::shared_ptr<HTMLTable>& rTab)
646 : m_pStartNode(nullptr)
647 , m_xTable(rTab)
648 {
649 InitCtor();
650 }
651
~HTMLTableCnts()652 HTMLTableCnts::~HTMLTableCnts()
653 {
654 m_xTable.reset(); // we don't need the tables anymore
655 m_pNext.reset();
656 }
657
Add(std::unique_ptr<HTMLTableCnts> pNewCnts)658 void HTMLTableCnts::Add( std::unique_ptr<HTMLTableCnts> pNewCnts )
659 {
660 HTMLTableCnts *pCnts = this;
661
662 while( pCnts->m_pNext )
663 pCnts = pCnts->m_pNext.get();
664
665 pCnts->m_pNext = std::move(pNewCnts);
666 }
667
SetTableBox(SwTableBox * pBox)668 inline void HTMLTableCnts::SetTableBox( SwTableBox *pBox )
669 {
670 OSL_ENSURE(m_xLayoutInfo.get(), "There is no layout info");
671 if (m_xLayoutInfo)
672 m_xLayoutInfo->SetTableBox(pBox);
673 }
674
CreateLayoutInfo()675 const std::shared_ptr<SwHTMLTableLayoutCnts>& HTMLTableCnts::CreateLayoutInfo()
676 {
677 if (!m_xLayoutInfo)
678 {
679 std::shared_ptr<SwHTMLTableLayoutCnts> xNextInfo;
680 if (m_pNext)
681 xNextInfo = m_pNext->CreateLayoutInfo();
682 std::shared_ptr<SwHTMLTableLayout> xTableInfo;
683 if (m_xTable)
684 xTableInfo = m_xTable->CreateLayoutInfo();
685 m_xLayoutInfo.reset(new SwHTMLTableLayoutCnts(m_pStartNode, xTableInfo, m_bNoBreak, xNextInfo));
686 }
687
688 return m_xLayoutInfo;
689 }
690
HTMLTableCell()691 HTMLTableCell::HTMLTableCell():
692 m_nValue(0),
693 m_nNumFormat(0),
694 m_nRowSpan(1),
695 m_nColSpan(1),
696 m_nWidth( 0 ),
697 m_eVertOrient( text::VertOrientation::NONE ),
698 m_bProtected(false),
699 m_bRelWidth( false ),
700 m_bHasNumFormat(false),
701 m_bHasValue(false),
702 m_bNoWrap(false),
703 mbCovered(false)
704 {}
705
Set(std::shared_ptr<HTMLTableCnts> const & rCnts,sal_uInt16 nRSpan,sal_uInt16 nCSpan,sal_Int16 eVert,std::shared_ptr<SvxBrushItem> const & rBrush,std::shared_ptr<SvxBoxItem> const & rBoxItem,bool bHasNF,sal_uInt32 nNF,bool bHasV,double nVal,bool bNWrap,bool bCovered)706 void HTMLTableCell::Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan,
707 sal_Int16 eVert, std::shared_ptr<SvxBrushItem> const& rBrush,
708 std::shared_ptr<SvxBoxItem> const& rBoxItem,
709 bool bHasNF, sal_uInt32 nNF, bool bHasV, double nVal,
710 bool bNWrap, bool bCovered )
711 {
712 m_xContents = rCnts;
713 m_nRowSpan = nRSpan;
714 m_nColSpan = nCSpan;
715 m_bProtected = false;
716 m_eVertOrient = eVert;
717 m_xBGBrush = rBrush;
718 m_xBoxItem = rBoxItem;
719
720 m_bHasNumFormat = bHasNF;
721 m_bHasValue = bHasV;
722 m_nNumFormat = nNF;
723 m_nValue = nVal;
724
725 m_bNoWrap = bNWrap;
726 mbCovered = bCovered;
727 }
728
SetWidth(sal_uInt16 nWdth,bool bRelWdth)729 inline void HTMLTableCell::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
730 {
731 m_nWidth = nWdth;
732 m_bRelWidth = bRelWdth;
733 }
734
SetProtected()735 void HTMLTableCell::SetProtected()
736 {
737 // The content of this cell doesn't have to be anchored anywhere else,
738 // since they're not gonna be deleted
739
740 m_xContents.reset();
741
742 // Copy background color
743 if (m_xBGBrush)
744 m_xBGBrush.reset(new SvxBrushItem(*m_xBGBrush));
745
746 m_nRowSpan = 1;
747 m_nColSpan = 1;
748 m_bProtected = true;
749 }
750
GetNumFormat(sal_uInt32 & rNumFormat) const751 inline bool HTMLTableCell::GetNumFormat( sal_uInt32& rNumFormat ) const
752 {
753 rNumFormat = m_nNumFormat;
754 return m_bHasNumFormat;
755 }
756
GetValue(double & rValue) const757 inline bool HTMLTableCell::GetValue( double& rValue ) const
758 {
759 rValue = m_nValue;
760 return m_bHasValue;
761 }
762
CreateLayoutInfo()763 std::unique_ptr<SwHTMLTableLayoutCell> HTMLTableCell::CreateLayoutInfo()
764 {
765 std::shared_ptr<SwHTMLTableLayoutCnts> xCntInfo;
766 if (m_xContents)
767 xCntInfo = m_xContents->CreateLayoutInfo();
768 return std::unique_ptr<SwHTMLTableLayoutCell>(new SwHTMLTableLayoutCell(xCntInfo, m_nRowSpan, m_nColSpan, m_nWidth,
769 m_bRelWidth, m_bNoWrap));
770 }
771
HTMLTableRow(sal_uInt16 const nCells)772 HTMLTableRow::HTMLTableRow(sal_uInt16 const nCells)
773 : m_aCells(nCells)
774 , eAdjust(SvxAdjust::End)
775 , nHeight(0)
776 , nEmptyRows(0)
777 , eVertOri(text::VertOrientation::TOP)
778 , bIsEndOfGroup(false)
779 , bBottomBorder(false)
780 {
781 assert(nCells == m_aCells.size() &&
782 "wrong Cell count in new HTML table row");
783 }
784
SetHeight(sal_uInt16 nHght)785 inline void HTMLTableRow::SetHeight( sal_uInt16 nHght )
786 {
787 if( nHght > nHeight )
788 nHeight = nHght;
789 }
790
GetCell(sal_uInt16 nCell) const791 const HTMLTableCell& HTMLTableRow::GetCell(sal_uInt16 nCell) const
792 {
793 OSL_ENSURE( nCell < m_aCells.size(),
794 "invalid cell index in HTML table row" );
795 return m_aCells.at(nCell);
796 }
797
Expand(sal_uInt16 nCells,bool bOneCell)798 void HTMLTableRow::Expand( sal_uInt16 nCells, bool bOneCell )
799 {
800 // This row will be filled with a single cell if bOneCell is set.
801 // This will only work for rows that don't allow adding cells!
802
803 sal_uInt16 nColSpan = nCells - m_aCells.size();
804 for (sal_uInt16 i = m_aCells.size(); i < nCells; ++i)
805 {
806 m_aCells.emplace_back();
807 if (bOneCell)
808 m_aCells.back().SetColSpan(nColSpan);
809 --nColSpan;
810 }
811
812 OSL_ENSURE(nCells == m_aCells.size(),
813 "wrong Cell count in expanded HTML table row");
814 }
815
Shrink(sal_uInt16 nCells)816 void HTMLTableRow::Shrink( sal_uInt16 nCells )
817 {
818 OSL_ENSURE(nCells < m_aCells.size(), "number of cells too large");
819
820 #if OSL_DEBUG_LEVEL > 0
821 sal_uInt16 const nEnd = m_aCells.size();
822 #endif
823 // The colspan of empty cells at the end has to be fixed to the new
824 // number of cells.
825 sal_uInt16 i=nCells;
826 while( i )
827 {
828 HTMLTableCell& rCell = m_aCells[--i];
829 if (!rCell.GetContents())
830 {
831 #if OSL_DEBUG_LEVEL > 0
832 OSL_ENSURE( rCell.GetColSpan() == nEnd - i,
833 "invalid col span for empty cell at row end" );
834 #endif
835 rCell.SetColSpan( nCells-i);
836 }
837 else
838 break;
839 }
840 #if OSL_DEBUG_LEVEL > 0
841 for( i=nCells; i<nEnd; i++ )
842 {
843 HTMLTableCell& rCell = m_aCells[i];
844 OSL_ENSURE( rCell.GetRowSpan() == 1,
845 "RowSpan of to be deleted cell is wrong" );
846 OSL_ENSURE( rCell.GetColSpan() == nEnd - i,
847 "ColSpan of to be deleted cell is wrong" );
848 OSL_ENSURE( !rCell.GetContents(), "To be deleted cell has content" );
849 }
850 #endif
851
852 m_aCells.erase(m_aCells.begin() + nCells, m_aCells.end());
853 }
854
HTMLTableColumn()855 HTMLTableColumn::HTMLTableColumn():
856 bIsEndOfGroup(false),
857 nWidth(0), bRelWidth(false),
858 eAdjust(SvxAdjust::End), eVertOri(text::VertOrientation::TOP),
859 bLeftBorder(false)
860 {
861 for(SwFrameFormat* & rp : aFrameFormats)
862 rp = nullptr;
863 }
864
SetWidth(sal_uInt16 nWdth,bool bRelWdth)865 inline void HTMLTableColumn::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
866 {
867 if( bRelWidth==bRelWdth )
868 {
869 if( nWdth > nWidth )
870 nWidth = nWdth;
871 }
872 else
873 nWidth = nWdth;
874 bRelWidth = bRelWdth;
875 }
876
CreateLayoutInfo()877 inline std::unique_ptr<SwHTMLTableLayoutColumn> HTMLTableColumn::CreateLayoutInfo()
878 {
879 return std::unique_ptr<SwHTMLTableLayoutColumn>(new SwHTMLTableLayoutColumn( nWidth, bRelWidth, bLeftBorder ));
880 }
881
GetFrameFormatIdx(bool bBorderLine,sal_Int16 eVertOrient)882 inline sal_uInt16 HTMLTableColumn::GetFrameFormatIdx( bool bBorderLine,
883 sal_Int16 eVertOrient )
884 {
885 OSL_ENSURE( text::VertOrientation::TOP != eVertOrient, "Top is not allowed" );
886 sal_uInt16 n = bBorderLine ? 3 : 0;
887 switch( eVertOrient )
888 {
889 case text::VertOrientation::CENTER: n+=1; break;
890 case text::VertOrientation::BOTTOM: n+=2; break;
891 default:
892 ;
893 }
894 return n;
895 }
896
SetFrameFormat(SwFrameFormat * pFormat,bool bBorderLine,sal_Int16 eVertOrient)897 inline void HTMLTableColumn::SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
898 sal_Int16 eVertOrient )
899 {
900 aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)] = pFormat;
901 }
902
GetFrameFormat(bool bBorderLine,sal_Int16 eVertOrient) const903 inline SwFrameFormat *HTMLTableColumn::GetFrameFormat( bool bBorderLine,
904 sal_Int16 eVertOrient ) const
905 {
906 return aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)];
907 }
908
InitCtor(const HTMLTableOptions & rOptions)909 void HTMLTable::InitCtor(const HTMLTableOptions& rOptions)
910 {
911 m_nRows = 0;
912 m_nCurrentRow = 0; m_nCurrentColumn = 0;
913
914 m_pBoxFormat = nullptr; m_pLineFormat = nullptr;
915 m_pLineFrameFormatNoHeight = nullptr;
916 m_xInheritedBackgroundBrush.reset();
917
918 m_pPrevStartNode = nullptr;
919 m_pSwTable = nullptr;
920
921 m_bTopBorder = false; m_bRightBorder = false;
922 m_bTopAllowed = true; m_bRightAllowed = true;
923 m_bFillerTopBorder = false; m_bFillerBottomBorder = false;
924 m_bInheritedLeftBorder = false; m_bInheritedRightBorder = false;
925 m_bBordersSet = false;
926 m_bForceFrame = false;
927 m_nHeadlineRepeat = 0;
928
929 m_nLeftMargin = 0;
930 m_nRightMargin = 0;
931
932 const Color& rBorderColor = rOptions.aBorderColor;
933
934 long nBorderOpt = static_cast<long>(rOptions.nBorder);
935 long nPWidth = nBorderOpt==USHRT_MAX ? NETSCAPE_DFLT_BORDER
936 : nBorderOpt;
937 long nPHeight = nBorderOpt==USHRT_MAX ? 0 : nBorderOpt;
938 SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
939
940 // nBorder tells the width of the border as it's used in the width calculation of NetScape
941 // If pOption->nBorder == USHRT_MAX, there wasn't a BORDER option given
942 // Nonetheless, a 1 pixel wide border will be used for width calculation
943 m_nBorder = static_cast<sal_uInt16>(nPWidth);
944 if( nBorderOpt==USHRT_MAX )
945 nPWidth = 0;
946
947 if ( rOptions.nCellSpacing != 0 )
948 {
949 m_aTopBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
950 }
951 m_aTopBorderLine.SetWidth( nPHeight );
952 m_aTopBorderLine.SetColor( rBorderColor );
953 m_aBottomBorderLine = m_aTopBorderLine;
954
955 if( nPWidth == nPHeight )
956 {
957 m_aLeftBorderLine = m_aTopBorderLine;
958 }
959 else
960 {
961 if ( rOptions.nCellSpacing != 0 )
962 {
963 m_aLeftBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
964 }
965 m_aLeftBorderLine.SetWidth( nPWidth );
966 m_aLeftBorderLine.SetColor( rBorderColor );
967 }
968 m_aRightBorderLine = m_aLeftBorderLine;
969
970 if( rOptions.nCellSpacing != 0 )
971 {
972 m_aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
973 m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 );
974 }
975 else
976 {
977 m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 );
978 }
979 m_aBorderLine.SetColor( rBorderColor );
980
981 if( m_nCellPadding )
982 {
983 if( m_nCellPadding==USHRT_MAX )
984 m_nCellPadding = MIN_BORDER_DIST; // default
985 else
986 {
987 m_nCellPadding = SwHTMLParser::ToTwips( m_nCellPadding );
988 if( m_nCellPadding<MIN_BORDER_DIST )
989 m_nCellPadding = MIN_BORDER_DIST;
990 }
991 }
992 if( m_nCellSpacing )
993 {
994 if( m_nCellSpacing==USHRT_MAX )
995 m_nCellSpacing = NETSCAPE_DFLT_CELLSPACING;
996 m_nCellSpacing = SwHTMLParser::ToTwips( m_nCellSpacing );
997 }
998
999 nPWidth = rOptions.nHSpace;
1000 nPHeight = rOptions.nVSpace;
1001 SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
1002 m_nHSpace = static_cast<sal_uInt16>(nPWidth);
1003 m_nVSpace = static_cast<sal_uInt16>(nPHeight);
1004
1005 m_bColSpec = false;
1006
1007 m_xBackgroundBrush.reset(m_pParser->CreateBrushItem(
1008 rOptions.bBGColor ? &(rOptions.aBGColor) : nullptr,
1009 rOptions.aBGImage, OUString(), OUString(), OUString()));
1010
1011 m_pContext = nullptr;
1012 m_xParentContents.reset();
1013
1014 m_aId = rOptions.aId;
1015 m_aClass = rOptions.aClass;
1016 m_aStyle = rOptions.aStyle;
1017 m_aDir = rOptions.aDir;
1018 }
1019
HTMLTable(SwHTMLParser * pPars,bool bParHead,bool bHasParentSec,bool bHasToFlw,const HTMLTableOptions & rOptions)1020 HTMLTable::HTMLTable(SwHTMLParser* pPars,
1021 bool bParHead,
1022 bool bHasParentSec, bool bHasToFlw,
1023 const HTMLTableOptions& rOptions) :
1024 m_aColumns(rOptions.nCols),
1025 m_nCols(rOptions.nCols),
1026 m_nFilledColumns( 0 ),
1027 m_nCellPadding(rOptions.nCellPadding),
1028 m_nCellSpacing(rOptions.nCellSpacing),
1029 m_nBoxes( 1 ),
1030 m_pCaptionStartNode( nullptr ),
1031 m_bTableAdjustOfTag( rOptions.bTableAdjust ),
1032 m_bIsParentHead( bParHead ),
1033 m_bHasParentSection( bHasParentSec ),
1034 m_bHasToFly( bHasToFlw ),
1035 m_bFixedCols( rOptions.nCols>0 ),
1036 m_bPrcWidth( rOptions.bPrcWidth ),
1037 m_pParser( pPars ),
1038 m_nWidth( rOptions.nWidth ),
1039 m_nHeight( rOptions.nHeight ),
1040 m_eTableAdjust( rOptions.eAdjust ),
1041 m_eVertOrientation( rOptions.eVertOri ),
1042 m_eFrame( rOptions.eFrame ),
1043 m_eRules( rOptions.eRules ),
1044 m_bTopCaption( false ),
1045 m_bFirstCell(true)
1046 {
1047 InitCtor(rOptions);
1048 m_pParser->RegisterHTMLTable(this);
1049 }
1050
DeregisterHTMLTable(HTMLTable * pOld)1051 void SwHTMLParser::DeregisterHTMLTable(HTMLTable* pOld)
1052 {
1053 if (pOld->m_xBox1)
1054 m_aOrphanedTableBoxes.emplace_back(std::move(pOld->m_xBox1));
1055 m_aTables.erase(std::remove(m_aTables.begin(), m_aTables.end(), pOld));
1056 }
1057
GetDoc() const1058 SwDoc* SwHTMLParser::GetDoc() const
1059 {
1060 return m_xDoc.get();
1061 }
1062
IsReqIF() const1063 bool SwHTMLParser::IsReqIF() const
1064 {
1065 return m_bReqIF;
1066 }
1067
~HTMLTable()1068 HTMLTable::~HTMLTable()
1069 {
1070 m_pParser->DeregisterHTMLTable(this);
1071
1072 m_pResizeDrawObjects.reset();
1073 m_pDrawObjectPrcWidths.reset();
1074
1075 m_pContext.reset();
1076
1077 // pLayoutInfo has either already been deleted or is now owned by SwTable
1078 }
1079
CreateLayoutInfo()1080 const std::shared_ptr<SwHTMLTableLayout>& HTMLTable::CreateLayoutInfo()
1081 {
1082 sal_uInt16 nW = m_bPrcWidth ? m_nWidth : SwHTMLParser::ToTwips( m_nWidth );
1083
1084 sal_uInt16 nBorderWidth = GetBorderWidth( m_aBorderLine, true );
1085 sal_uInt16 nLeftBorderWidth =
1086 m_aColumns[0].bLeftBorder ? GetBorderWidth(m_aLeftBorderLine, true) : 0;
1087 sal_uInt16 nRightBorderWidth =
1088 m_bRightBorder ? GetBorderWidth( m_aRightBorderLine, true ) : 0;
1089
1090 m_xLayoutInfo.reset(new SwHTMLTableLayout(
1091 m_pSwTable,
1092 m_nRows, m_nCols, m_bFixedCols, m_bColSpec,
1093 nW, m_bPrcWidth, m_nBorder, m_nCellPadding,
1094 m_nCellSpacing, m_eTableAdjust,
1095 m_nLeftMargin, m_nRightMargin,
1096 nBorderWidth, nLeftBorderWidth, nRightBorderWidth));
1097
1098 bool bExportable = true;
1099 sal_uInt16 i;
1100 for( i=0; i<m_nRows; i++ )
1101 {
1102 HTMLTableRow& rRow = m_aRows[i];
1103 for( sal_uInt16 j=0; j<m_nCols; j++ )
1104 {
1105 m_xLayoutInfo->SetCell(rRow.GetCell(j).CreateLayoutInfo(), i, j);
1106 SwHTMLTableLayoutCell* pLayoutCell = m_xLayoutInfo->GetCell(i, j );
1107
1108 if( bExportable )
1109 {
1110 const std::shared_ptr<SwHTMLTableLayoutCnts>& rLayoutCnts =
1111 pLayoutCell->GetContents();
1112 bExportable = !rLayoutCnts ||
1113 (rLayoutCnts->GetStartNode() && !rLayoutCnts->GetNext());
1114 }
1115 }
1116 }
1117
1118 m_xLayoutInfo->SetExportable( bExportable );
1119
1120 for( i=0; i<m_nCols; i++ )
1121 m_xLayoutInfo->SetColumn(m_aColumns[i].CreateLayoutInfo(), i);
1122
1123 return m_xLayoutInfo;
1124 }
1125
SetCaption(const SwStartNode * pStNd,bool bTop)1126 inline void HTMLTable::SetCaption( const SwStartNode *pStNd, bool bTop )
1127 {
1128 m_pCaptionStartNode = pStNd;
1129 m_bTopCaption = bTop;
1130 }
1131
FixRowSpan(sal_uInt16 nRow,sal_uInt16 nCol,const HTMLTableCnts * pCnts)1132 void HTMLTable::FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol,
1133 const HTMLTableCnts *pCnts )
1134 {
1135 sal_uInt16 nRowSpan=1;
1136 while (true)
1137 {
1138 HTMLTableCell& rCell = GetCell(nRow, nCol);
1139 if (rCell.GetContents().get() != pCnts)
1140 break;
1141 rCell.SetRowSpan(nRowSpan);
1142 if (m_xLayoutInfo)
1143 m_xLayoutInfo->GetCell(nRow,nCol)->SetRowSpan(nRowSpan);
1144
1145 if( !nRow ) break;
1146 nRowSpan++; nRow--;
1147 }
1148 }
1149
ProtectRowSpan(sal_uInt16 nRow,sal_uInt16 nCol,sal_uInt16 nRowSpan)1150 void HTMLTable::ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan )
1151 {
1152 for( sal_uInt16 i=0; i<nRowSpan; i++ )
1153 {
1154 GetCell(nRow+i,nCol).SetProtected();
1155 if (m_xLayoutInfo)
1156 m_xLayoutInfo->GetCell(nRow+i,nCol)->SetProtected();
1157 }
1158 }
1159
1160 // Search the SwStartNode of the last used predecessor box
GetPrevBoxStartNode(sal_uInt16 nRow,sal_uInt16 nCol) const1161 const SwStartNode* HTMLTable::GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCol ) const
1162 {
1163 const HTMLTableCnts *pPrevCnts = nullptr;
1164
1165 if( 0==nRow )
1166 {
1167 // always the predecessor cell
1168 if( nCol>0 )
1169 pPrevCnts = GetCell(0, nCol - 1).GetContents().get();
1170 else
1171 return m_pPrevStartNode;
1172 }
1173 else if( USHRT_MAX==nRow && USHRT_MAX==nCol )
1174 // contents of preceding cell
1175 pPrevCnts = GetCell(m_nRows - 1, m_nCols - 1).GetContents().get();
1176 else
1177 {
1178 sal_uInt16 i;
1179 const HTMLTableRow& rPrevRow = m_aRows[nRow-1];
1180
1181 // maybe a cell in the current row
1182 i = nCol;
1183 while( i )
1184 {
1185 i--;
1186 if( 1 == rPrevRow.GetCell(i).GetRowSpan() )
1187 {
1188 pPrevCnts = GetCell(nRow, i).GetContents().get();
1189 break;
1190 }
1191 }
1192
1193 // otherwise the last filled cell of the row before
1194 if( !pPrevCnts )
1195 {
1196 i = m_nCols;
1197 while( !pPrevCnts && i )
1198 {
1199 i--;
1200 pPrevCnts = rPrevRow.GetCell(i).GetContents().get();
1201 }
1202 }
1203 }
1204 OSL_ENSURE( pPrevCnts, "No previous filled cell found" );
1205 if( !pPrevCnts )
1206 {
1207 pPrevCnts = GetCell(0, 0).GetContents().get();
1208 if( !pPrevCnts )
1209 return m_pPrevStartNode;
1210 }
1211
1212 while( pPrevCnts->Next() )
1213 pPrevCnts = pPrevCnts->Next();
1214
1215 const SwStartNode* pRet = pPrevCnts->GetStartNode();
1216 if (pRet)
1217 return pRet;
1218 HTMLTable* pTable = pPrevCnts->GetTable().get();
1219 if (!pTable)
1220 return nullptr;
1221 return pTable->GetPrevBoxStartNode(USHRT_MAX, USHRT_MAX);
1222 }
1223
IsBoxEmpty(const SwTableBox * pBox)1224 static bool IsBoxEmpty( const SwTableBox *pBox )
1225 {
1226 const SwStartNode *pSttNd = pBox->GetSttNd();
1227 if( pSttNd &&
1228 pSttNd->GetIndex() + 2 == pSttNd->EndOfSectionIndex() )
1229 {
1230 const SwContentNode *pCNd =
1231 pSttNd->GetNodes()[pSttNd->GetIndex()+1]->GetContentNode();
1232 if( pCNd && !pCNd->Len() )
1233 return true;
1234 }
1235
1236 return false;
1237 }
1238
GetTopCellSpace(sal_uInt16 nRow) const1239 sal_uInt16 HTMLTable::GetTopCellSpace( sal_uInt16 nRow ) const
1240 {
1241 sal_uInt16 nSpace = m_nCellPadding;
1242
1243 if( nRow == 0 )
1244 {
1245 nSpace += m_nBorder + m_nCellSpacing;
1246 }
1247
1248 return nSpace;
1249 }
1250
GetBottomCellSpace(sal_uInt16 nRow,sal_uInt16 nRowSpan) const1251 sal_uInt16 HTMLTable::GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const
1252 {
1253 sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
1254
1255 if( nRow+nRowSpan == m_nRows )
1256 {
1257 nSpace = nSpace + m_nBorder;
1258 }
1259
1260 return nSpace;
1261 }
1262
FixFrameFormat(SwTableBox * pBox,sal_uInt16 nRow,sal_uInt16 nCol,sal_uInt16 nRowSpan,sal_uInt16 nColSpan,bool bFirstPara,bool bLastPara) const1263 void HTMLTable::FixFrameFormat( SwTableBox *pBox,
1264 sal_uInt16 nRow, sal_uInt16 nCol,
1265 sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
1266 bool bFirstPara, bool bLastPara ) const
1267 {
1268 SwFrameFormat *pFrameFormat = nullptr; // frame::Frame format
1269 sal_Int16 eVOri = text::VertOrientation::NONE;
1270 const SvxBrushItem *pBGBrushItem = nullptr; // background
1271 std::shared_ptr<SvxBoxItem> pBoxItem;
1272 bool bTopLine = false, bBottomLine = false, bLastBottomLine = false;
1273 bool bReUsable = false; // Format reusable?
1274 sal_uInt16 nEmptyRows = 0;
1275 bool bHasNumFormat = false;
1276 bool bHasValue = false;
1277 sal_uInt32 nNumFormat = 0;
1278 double nValue = 0.0;
1279
1280 const HTMLTableColumn& rColumn = m_aColumns[nCol];
1281
1282 if( pBox->GetSttNd() )
1283 {
1284 // Determine background color/graphic
1285 const HTMLTableCell& rCell = GetCell(nRow, nCol);
1286 pBoxItem = rCell.GetBoxItem();
1287 pBGBrushItem = rCell.GetBGBrush().get();
1288 if( !pBGBrushItem )
1289 {
1290 // If a cell spans multiple rows, a background to that row should be copied to the cell.
1291 if (nRowSpan > 1)
1292 {
1293 pBGBrushItem = m_aRows[nRow].GetBGBrush().get();
1294 }
1295 }
1296
1297 bTopLine = 0==nRow && m_bTopBorder && bFirstPara;
1298 if (m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
1299 {
1300 nEmptyRows = m_aRows[nRow+nRowSpan-1].GetEmptyRows();
1301 if( nRow+nRowSpan == m_nRows )
1302 bLastBottomLine = true;
1303 else
1304 bBottomLine = true;
1305 }
1306
1307 eVOri = rCell.GetVertOri();
1308 bHasNumFormat = rCell.GetNumFormat( nNumFormat );
1309 if( bHasNumFormat )
1310 bHasValue = rCell.GetValue( nValue );
1311
1312 if( nColSpan==1 && !bTopLine && !bLastBottomLine && !nEmptyRows &&
1313 !pBGBrushItem && !bHasNumFormat && !pBoxItem)
1314 {
1315 pFrameFormat = rColumn.GetFrameFormat( bBottomLine, eVOri );
1316 bReUsable = !pFrameFormat;
1317 }
1318 }
1319
1320 if( !pFrameFormat )
1321 {
1322 pFrameFormat = pBox->ClaimFrameFormat();
1323
1324 // calculate width of the box
1325 SwTwips nFrameWidth = static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol)
1326 ->GetRelColWidth());
1327 for( sal_uInt16 i=1; i<nColSpan; i++ )
1328 nFrameWidth += static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol+i)
1329 ->GetRelColWidth());
1330
1331 // Only set the border on edit boxes.
1332 // On setting the upper and lower border, keep in mind if
1333 // it's the first or the last paragraph of the cell
1334 if( pBox->GetSttNd() )
1335 {
1336 bool bSet = (m_nCellPadding > 0);
1337
1338 SvxBoxItem aBoxItem( RES_BOX );
1339 long nInnerFrameWidth = nFrameWidth;
1340
1341 if( bTopLine )
1342 {
1343 aBoxItem.SetLine( &m_aTopBorderLine, SvxBoxItemLine::TOP );
1344 bSet = true;
1345 }
1346 if( bLastBottomLine )
1347 {
1348 aBoxItem.SetLine( &m_aBottomBorderLine, SvxBoxItemLine::BOTTOM );
1349 bSet = true;
1350 }
1351 else if( bBottomLine )
1352 {
1353 if( nEmptyRows && !m_aBorderLine.GetInWidth() )
1354 {
1355 // For now, empty rows can only be emulated by thick lines, if it's a single line
1356 SvxBorderLine aThickBorderLine( m_aBorderLine );
1357
1358 sal_uInt16 nBorderWidth = m_aBorderLine.GetOutWidth();
1359 nBorderWidth *= (nEmptyRows + 1);
1360 aThickBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
1361 aThickBorderLine.SetWidth( nBorderWidth );
1362 aBoxItem.SetLine( &aThickBorderLine, SvxBoxItemLine::BOTTOM );
1363 }
1364 else
1365 {
1366 aBoxItem.SetLine( &m_aBorderLine, SvxBoxItemLine::BOTTOM );
1367 }
1368 bSet = true;
1369 }
1370 if (m_aColumns[nCol].bLeftBorder)
1371 {
1372 const SvxBorderLine& rBorderLine =
1373 0==nCol ? m_aLeftBorderLine : m_aBorderLine;
1374 aBoxItem.SetLine( &rBorderLine, SvxBoxItemLine::LEFT );
1375 nInnerFrameWidth -= GetBorderWidth( rBorderLine );
1376 bSet = true;
1377 }
1378 if( m_bRightBorder )
1379 {
1380 aBoxItem.SetLine( &m_aRightBorderLine, SvxBoxItemLine::RIGHT );
1381 nInnerFrameWidth -= GetBorderWidth( m_aRightBorderLine );
1382 bSet = true;
1383 }
1384
1385 if (pBoxItem)
1386 {
1387 pFrameFormat->SetFormatAttr( *pBoxItem );
1388 }
1389 else if (bSet)
1390 {
1391 // BorderDist is not part of a cell with fixed width
1392 sal_uInt16 nBDist = static_cast< sal_uInt16 >(
1393 (2*m_nCellPadding <= nInnerFrameWidth) ? m_nCellPadding
1394 : (nInnerFrameWidth / 2) );
1395 // We only set the item if there's a border or a border distance
1396 // If the latter is missing, there's gonna be a border and we'll have to set the distance
1397 aBoxItem.SetAllDistances(nBDist ? nBDist : MIN_BORDER_DIST);
1398 pFrameFormat->SetFormatAttr( aBoxItem );
1399 }
1400 else
1401 pFrameFormat->ResetFormatAttr( RES_BOX );
1402
1403 if( pBGBrushItem )
1404 {
1405 pFrameFormat->SetFormatAttr( *pBGBrushItem );
1406 }
1407 else
1408 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1409
1410 // Only set format if there's a value or the box is empty
1411 if( bHasNumFormat && (bHasValue || IsBoxEmpty(pBox)) )
1412 {
1413 bool bLock = pFrameFormat->GetDoc()->GetNumberFormatter()
1414 ->IsTextFormat( nNumFormat );
1415 SfxItemSet aItemSet( *pFrameFormat->GetAttrSet().GetPool(),
1416 svl::Items<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{} );
1417 SvxAdjust eAdjust = SvxAdjust::End;
1418 SwContentNode *pCNd = nullptr;
1419 if( !bLock )
1420 {
1421 const SwStartNode *pSttNd = pBox->GetSttNd();
1422 pCNd = pSttNd->GetNodes()[pSttNd->GetIndex()+1]
1423 ->GetContentNode();
1424 const SfxPoolItem *pItem;
1425 if( pCNd && pCNd->HasSwAttrSet() &&
1426 SfxItemState::SET==pCNd->GetpSwAttrSet()->GetItemState(
1427 RES_PARATR_ADJUST, false, &pItem ) )
1428 {
1429 eAdjust = static_cast<const SvxAdjustItem *>(pItem)
1430 ->GetAdjust();
1431 }
1432 }
1433 aItemSet.Put( SwTableBoxNumFormat(nNumFormat) );
1434 if( bHasValue )
1435 aItemSet.Put( SwTableBoxValue(nValue) );
1436
1437 if( bLock )
1438 pFrameFormat->LockModify();
1439 pFrameFormat->SetFormatAttr( aItemSet );
1440 if( bLock )
1441 pFrameFormat->UnlockModify();
1442 else if( pCNd && SvxAdjust::End != eAdjust )
1443 {
1444 SvxAdjustItem aAdjItem( eAdjust, RES_PARATR_ADJUST );
1445 pCNd->SetAttr( aAdjItem );
1446 }
1447 }
1448 else
1449 pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1450
1451 OSL_ENSURE( eVOri != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
1452 if( text::VertOrientation::NONE != eVOri )
1453 {
1454 pFrameFormat->SetFormatAttr( SwFormatVertOrient( 0, eVOri ) );
1455 }
1456 else
1457 pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
1458
1459 if( bReUsable )
1460 const_cast<HTMLTableColumn&>(rColumn).SetFrameFormat(pFrameFormat, bBottomLine, eVOri);
1461 }
1462 else
1463 {
1464 pFrameFormat->ResetFormatAttr( RES_BOX );
1465 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1466 pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
1467 pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1468 }
1469
1470 if (m_pParser->IsReqIF())
1471 {
1472 // ReqIF case, cells would have no formatting. Apply the default
1473 // table autoformat on them, so imported and UI-created tables look
1474 // the same.
1475 SwTableAutoFormatTable& rTable = m_pParser->GetDoc()->GetTableStyles();
1476 SwTableAutoFormat* pTableFormat = rTable.FindAutoFormat(
1477 SwStyleNameMapper::GetUIName(RES_POOLTABSTYLE_DEFAULT, OUString()));
1478 if (pTableFormat)
1479 {
1480 sal_uInt8 nPos = SwTableAutoFormat::CountPos(nCol, m_nCols, nRow, m_nRows);
1481 pTableFormat->UpdateToSet(nPos, m_nRows==1, m_nCols==1,
1482 const_cast<SfxItemSet&>(static_cast<SfxItemSet const&>(
1483 pFrameFormat->GetAttrSet())),
1484 SwTableAutoFormatUpdateFlags::Box,
1485 pFrameFormat->GetDoc()->GetNumberFormatter());
1486 }
1487 }
1488 }
1489 else
1490 {
1491 OSL_ENSURE( pBox->GetSttNd() ||
1492 SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState(
1493 RES_VERT_ORIENT, false ),
1494 "Box without content has vertical orientation" );
1495 pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFrameFormat) );
1496 }
1497
1498 }
1499
NewTableBox(const SwStartNode * pStNd,SwTableLine * pUpper) const1500 SwTableBox *HTMLTable::NewTableBox( const SwStartNode *pStNd,
1501 SwTableLine *pUpper ) const
1502 {
1503 SwTableBox *pBox;
1504
1505 if (m_xBox1 && m_xBox1->GetSttNd() == pStNd)
1506 {
1507 // If the StartNode is the StartNode of the initially created box, we take that box
1508 pBox = const_cast<HTMLTable*>(this)->m_xBox1.release();
1509 pBox->SetUpper(pUpper);
1510 }
1511 else
1512 pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper );
1513
1514 return pBox;
1515 }
1516
ResetLineFrameFormatAttrs(SwFrameFormat * pFrameFormat)1517 static void ResetLineFrameFormatAttrs( SwFrameFormat *pFrameFormat )
1518 {
1519 pFrameFormat->ResetFormatAttr( RES_FRM_SIZE );
1520 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1521 OSL_ENSURE( SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState(
1522 RES_VERT_ORIENT, false ),
1523 "Cell has vertical orientation" );
1524 }
1525
1526 // !!! could be simplified
MakeTableLine(SwTableBox * pUpper,sal_uInt16 nTopRow,sal_uInt16 nLeftCol,sal_uInt16 nBottomRow,sal_uInt16 nRightCol)1527 SwTableLine *HTMLTable::MakeTableLine( SwTableBox *pUpper,
1528 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
1529 sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
1530 {
1531 SwTableLine *pLine;
1532 if (!pUpper && 0 == nTopRow)
1533 pLine = (m_pSwTable->GetTabLines())[0];
1534 else
1535 pLine = new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight
1536 : m_pLineFormat,
1537 0, pUpper );
1538
1539 const HTMLTableRow& rTopRow = m_aRows[nTopRow];
1540 sal_uInt16 nRowHeight = rTopRow.GetHeight();
1541 const SvxBrushItem *pBGBrushItem = nullptr;
1542 if (nTopRow > 0 || nBottomRow < m_nRows)
1543 {
1544 // It doesn't make sense to set a color on a line,
1545 // if it's the outermost and simultaneously sole line of a table in a table
1546 pBGBrushItem = rTopRow.GetBGBrush().get();
1547 }
1548 if( nTopRow==nBottomRow-1 && (nRowHeight || pBGBrushItem) )
1549 {
1550 SwTableLineFormat *pFrameFormat = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1551 ResetLineFrameFormatAttrs( pFrameFormat );
1552
1553 if( nRowHeight )
1554 {
1555 // set table height. Since it's a minimum height it can be calculated like in Netscape,
1556 // so without considering the actual border width
1557 nRowHeight += GetTopCellSpace( nTopRow ) +
1558 GetBottomCellSpace( nTopRow, 1 );
1559
1560 pFrameFormat->SetFormatAttr( SwFormatFrameSize( ATT_MIN_SIZE, 0, nRowHeight ) );
1561 }
1562
1563 if( pBGBrushItem )
1564 {
1565 pFrameFormat->SetFormatAttr( *pBGBrushItem );
1566 }
1567
1568 }
1569 else if( !m_pLineFrameFormatNoHeight )
1570 {
1571 // else, we'll have to remove the height from the attribute and remember the format
1572 m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1573
1574 ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
1575 }
1576
1577 SwTableBoxes& rBoxes = pLine->GetTabBoxes();
1578
1579 sal_uInt16 nStartCol = nLeftCol;
1580 while( nStartCol<nRightCol )
1581 {
1582 sal_uInt16 nCol = nStartCol;
1583 sal_uInt16 nSplitCol = nRightCol;
1584 bool bSplitted = false;
1585 while( !bSplitted )
1586 {
1587 OSL_ENSURE( nCol < nRightCol, "Gone too far" );
1588
1589 HTMLTableCell& rCell = GetCell(nTopRow,nCol);
1590 const bool bSplit = 1 == rCell.GetColSpan();
1591
1592 OSL_ENSURE((nCol != nRightCol-1) || bSplit, "Split-Flag wrong");
1593 if( bSplit )
1594 {
1595 SwTableBox* pBox = nullptr;
1596 HTMLTableCell& rCell2 = GetCell(nTopRow, nStartCol);
1597 if (rCell2.GetColSpan() == (nCol+1-nStartCol))
1598 {
1599 // The HTML tables represent a box. So we need to split behind that box
1600 nSplitCol = nCol + 1;
1601
1602 long nBoxRowSpan = rCell2.GetRowSpan();
1603 if (!rCell2.GetContents() || rCell2.IsCovered())
1604 {
1605 if (rCell2.IsCovered())
1606 nBoxRowSpan = -1 * nBoxRowSpan;
1607
1608 const SwStartNode* pPrevStartNd =
1609 GetPrevBoxStartNode( nTopRow, nStartCol );
1610 std::shared_ptr<HTMLTableCnts> xCnts(new HTMLTableCnts(
1611 m_pParser->InsertTableSection(pPrevStartNd)));
1612 const std::shared_ptr<SwHTMLTableLayoutCnts> xCntsLayoutInfo =
1613 xCnts->CreateLayoutInfo();
1614
1615 rCell2.SetContents(xCnts);
1616 SwHTMLTableLayoutCell *pCurrCell = m_xLayoutInfo->GetCell(nTopRow, nStartCol);
1617 pCurrCell->SetContents(xCntsLayoutInfo);
1618 if( nBoxRowSpan < 0 )
1619 pCurrCell->SetRowSpan( 0 );
1620
1621 // check COLSPAN if needed
1622 for( sal_uInt16 j=nStartCol+1; j<nSplitCol; j++ )
1623 {
1624 GetCell(nTopRow, j).SetContents(xCnts);
1625 m_xLayoutInfo->GetCell(nTopRow, j)
1626 ->SetContents(xCntsLayoutInfo);
1627 }
1628 }
1629
1630 pBox = MakeTableBox(pLine, rCell2.GetContents().get(),
1631 nTopRow, nStartCol,
1632 nBottomRow, nSplitCol);
1633
1634 if (1 != nBoxRowSpan && pBox)
1635 pBox->setRowSpan( nBoxRowSpan );
1636
1637 bSplitted = true;
1638 }
1639
1640 OSL_ENSURE( pBox, "Colspan trouble" );
1641
1642 if( pBox )
1643 rBoxes.push_back( pBox );
1644 }
1645 nCol++;
1646 }
1647 nStartCol = nSplitCol;
1648 }
1649
1650 return pLine;
1651 }
1652
MakeTableBox(SwTableLine * pUpper,HTMLTableCnts * pCnts,sal_uInt16 nTopRow,sal_uInt16 nLeftCol,sal_uInt16 nBottomRow,sal_uInt16 nRightCol)1653 SwTableBox *HTMLTable::MakeTableBox( SwTableLine *pUpper,
1654 HTMLTableCnts *pCnts,
1655 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
1656 sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
1657 {
1658 SwTableBox *pBox;
1659 sal_uInt16 nColSpan = nRightCol - nLeftCol;
1660 sal_uInt16 nRowSpan = nBottomRow - nTopRow;
1661
1662 if( !pCnts->Next() )
1663 {
1664 // just one content section
1665 if( pCnts->GetStartNode() )
1666 {
1667 // ... that's not a table
1668 pBox = NewTableBox( pCnts->GetStartNode(), pUpper );
1669 pCnts->SetTableBox( pBox );
1670 }
1671 else if (HTMLTable* pTable = pCnts->GetTable().get())
1672 {
1673 pTable->InheritVertBorders( this, nLeftCol,
1674 nRightCol-nLeftCol );
1675 // ... that's a table. We'll build a new box and put the rows of the table
1676 // in the rows of the box
1677 pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
1678 sal_uInt16 nAbs, nRel;
1679 m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel );
1680 sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol, nColSpan );
1681 sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol, nColSpan );
1682 sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan );
1683 pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace, nRSpace,
1684 nInhSpace );
1685 }
1686 else
1687 {
1688 return nullptr;
1689 }
1690 }
1691 else
1692 {
1693 // multiple content sections: we'll build a box with rows
1694 pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
1695 SwTableLines& rLines = pBox->GetTabLines();
1696 bool bFirstPara = true;
1697
1698 while( pCnts )
1699 {
1700 if( pCnts->GetStartNode() )
1701 {
1702 // normal paragraphs are gonna be boxes in a row
1703 SwTableLine *pLine =
1704 new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight
1705 : m_pLineFormat, 0, pBox );
1706 if( !m_pLineFrameFormatNoHeight )
1707 {
1708 // If there's no line format without height yet, we can use that one
1709 m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1710
1711 ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
1712 }
1713
1714 SwTableBox* pCntBox = NewTableBox( pCnts->GetStartNode(),
1715 pLine );
1716 pCnts->SetTableBox( pCntBox );
1717 FixFrameFormat( pCntBox, nTopRow, nLeftCol, nRowSpan, nColSpan,
1718 bFirstPara, nullptr==pCnts->Next() );
1719 pLine->GetTabBoxes().push_back( pCntBox );
1720
1721 rLines.push_back( pLine );
1722 }
1723 else
1724 {
1725 pCnts->GetTable()->InheritVertBorders( this, nLeftCol,
1726 nRightCol-nLeftCol );
1727 // Tables are entered directly
1728 sal_uInt16 nAbs, nRel;
1729 m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel );
1730 sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol,
1731 nColSpan );
1732 sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol,
1733 nColSpan );
1734 sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan );
1735 pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace,
1736 nRSpace, nInhSpace );
1737 }
1738
1739 pCnts = pCnts->Next();
1740 bFirstPara = false;
1741 }
1742 }
1743
1744 FixFrameFormat( pBox, nTopRow, nLeftCol, nRowSpan, nColSpan );
1745
1746 return pBox;
1747 }
1748
InheritBorders(const HTMLTable * pParent,sal_uInt16 nRow,sal_uInt16 nCol,sal_uInt16 nRowSpan,bool bFirstPara,bool bLastPara)1749 void HTMLTable::InheritBorders( const HTMLTable *pParent,
1750 sal_uInt16 nRow, sal_uInt16 nCol,
1751 sal_uInt16 nRowSpan,
1752 bool bFirstPara, bool bLastPara )
1753 {
1754 OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
1755 "Was CloseTable not called?" );
1756
1757 // The child table needs a border, if the surrounding cell has a margin on that side.
1758 // The upper/lower border is only set if the table is the first/last paragraph in that cell
1759 // It can't be determined if a border for that table is needed or possible for the left or right side,
1760 // since that's depending on if filler cells are gonna be added. We'll only collect info for now
1761
1762 if( 0==nRow && pParent->m_bTopBorder && bFirstPara )
1763 {
1764 m_bTopBorder = true;
1765 m_bFillerTopBorder = true; // fillers get a border too
1766 m_aTopBorderLine = pParent->m_aTopBorderLine;
1767 }
1768 if (pParent->m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
1769 {
1770 m_aRows[m_nRows-1].SetBottomBorder(true);
1771 m_bFillerBottomBorder = true; // fillers get a border too
1772 m_aBottomBorderLine =
1773 nRow+nRowSpan==pParent->m_nRows ? pParent->m_aBottomBorderLine
1774 : pParent->m_aBorderLine;
1775 }
1776
1777 // The child table mustn't get an upper or lower border, if that's already done by the surrounding table
1778 // It can get an upper border if the table is not the first paragraph in that cell
1779 m_bTopAllowed = ( !bFirstPara || (pParent->m_bTopAllowed &&
1780 (0==nRow || !pParent->m_aRows[nRow-1].GetBottomBorder())) );
1781
1782 // The child table has to inherit the color of the cell it's contained in, if it doesn't have one
1783 const SvxBrushItem *pInhBG = pParent->GetCell(nRow, nCol).GetBGBrush().get();
1784 if( !pInhBG && pParent != this &&
1785 pParent->GetCell(nRow,nCol).GetRowSpan() == pParent->m_nRows )
1786 {
1787 // the whole surrounding table is a table in a table and consists only of a single line
1788 // that's gonna be GC-ed (correctly). That's why the background of that line is copied.
1789 pInhBG = pParent->m_aRows[nRow].GetBGBrush().get();
1790 if( !pInhBG )
1791 pInhBG = pParent->GetBGBrush().get();
1792 if( !pInhBG )
1793 pInhBG = pParent->GetInhBGBrush().get();
1794 }
1795 if( pInhBG )
1796 m_xInheritedBackgroundBrush.reset(new SvxBrushItem(*pInhBG));
1797 }
1798
InheritVertBorders(const HTMLTable * pParent,sal_uInt16 nCol,sal_uInt16 nColSpan)1799 void HTMLTable::InheritVertBorders( const HTMLTable *pParent,
1800 sal_uInt16 nCol, sal_uInt16 nColSpan )
1801 {
1802 sal_uInt16 nInhLeftBorderWidth = 0;
1803 sal_uInt16 nInhRightBorderWidth = 0;
1804
1805 if( nCol+nColSpan==pParent->m_nCols && pParent->m_bRightBorder )
1806 {
1807 m_bInheritedRightBorder = true; // just remember for now
1808 m_aInheritedRightBorderLine = pParent->m_aRightBorderLine;
1809 nInhRightBorderWidth =
1810 GetBorderWidth( m_aInheritedRightBorderLine, true ) + MIN_BORDER_DIST;
1811 }
1812
1813 if (pParent->m_aColumns[nCol].bLeftBorder)
1814 {
1815 m_bInheritedLeftBorder = true; // just remember for now
1816 m_aInheritedLeftBorderLine = 0==nCol ? pParent->m_aLeftBorderLine
1817 : pParent->m_aBorderLine;
1818 nInhLeftBorderWidth =
1819 GetBorderWidth( m_aInheritedLeftBorderLine, true ) + MIN_BORDER_DIST;
1820 }
1821
1822 if( !m_bInheritedLeftBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) )
1823 nInhLeftBorderWidth = 2 * MIN_BORDER_DIST;
1824 if( !m_bInheritedRightBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) )
1825 nInhRightBorderWidth = 2 * MIN_BORDER_DIST;
1826 m_xLayoutInfo->SetInhBorderWidths( nInhLeftBorderWidth,
1827 nInhRightBorderWidth );
1828
1829 m_bRightAllowed = ( pParent->m_bRightAllowed &&
1830 (nCol+nColSpan==pParent->m_nCols ||
1831 !pParent->m_aColumns[nCol+nColSpan].bLeftBorder) );
1832 }
1833
SetBorders()1834 void HTMLTable::SetBorders()
1835 {
1836 sal_uInt16 i;
1837 for( i=1; i<m_nCols; i++ )
1838 if( HTMLTableRules::All==m_eRules || HTMLTableRules::Cols==m_eRules ||
1839 ((HTMLTableRules::Rows==m_eRules || HTMLTableRules::Groups==m_eRules) &&
1840 m_aColumns[i-1].IsEndOfGroup()))
1841 {
1842 m_aColumns[i].bLeftBorder = true;
1843 }
1844
1845 for( i=0; i<m_nRows-1; i++ )
1846 if( HTMLTableRules::All==m_eRules || HTMLTableRules::Rows==m_eRules ||
1847 ((HTMLTableRules::Cols==m_eRules || HTMLTableRules::Groups==m_eRules) &&
1848 m_aRows[i].IsEndOfGroup()))
1849 {
1850 m_aRows[i].SetBottomBorder(true);
1851 }
1852
1853 if( m_bTopAllowed && (HTMLTableFrame::Above==m_eFrame || HTMLTableFrame::HSides==m_eFrame ||
1854 HTMLTableFrame::Box==m_eFrame) )
1855 m_bTopBorder = true;
1856 if( HTMLTableFrame::Below==m_eFrame || HTMLTableFrame::HSides==m_eFrame ||
1857 HTMLTableFrame::Box==m_eFrame )
1858 {
1859 m_aRows[m_nRows-1].SetBottomBorder(true);
1860 }
1861 if( HTMLTableFrame::RHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame ||
1862 HTMLTableFrame::Box==m_eFrame )
1863 m_bRightBorder = true;
1864 if( HTMLTableFrame::LHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame || HTMLTableFrame::Box==m_eFrame )
1865 {
1866 m_aColumns[0].bLeftBorder = true;
1867 }
1868
1869 for( i=0; i<m_nRows; i++ )
1870 {
1871 HTMLTableRow& rRow = m_aRows[i];
1872 for (sal_uInt16 j=0; j<m_nCols; ++j)
1873 {
1874 HTMLTableCell& rCell = rRow.GetCell(j);
1875 if (rCell.GetContents())
1876 {
1877 HTMLTableCnts *pCnts = rCell.GetContents().get();
1878 bool bFirstPara = true;
1879 while( pCnts )
1880 {
1881 HTMLTable *pTable = pCnts->GetTable().get();
1882 if( pTable && !pTable->BordersSet() )
1883 {
1884 pTable->InheritBorders(this, i, j,
1885 rCell.GetRowSpan(),
1886 bFirstPara,
1887 nullptr==pCnts->Next());
1888 pTable->SetBorders();
1889 }
1890 bFirstPara = false;
1891 pCnts = pCnts->Next();
1892 }
1893 }
1894 }
1895 }
1896
1897 m_bBordersSet = true;
1898 }
1899
GetBorderWidth(const SvxBorderLine & rBLine,bool bWithDistance) const1900 sal_uInt16 HTMLTable::GetBorderWidth( const SvxBorderLine& rBLine,
1901 bool bWithDistance ) const
1902 {
1903 sal_uInt16 nBorderWidth = rBLine.GetWidth();
1904 if( bWithDistance )
1905 {
1906 if( m_nCellPadding )
1907 nBorderWidth = nBorderWidth + m_nCellPadding;
1908 else if( nBorderWidth )
1909 nBorderWidth = nBorderWidth + MIN_BORDER_DIST;
1910 }
1911
1912 return nBorderWidth;
1913 }
1914
GetCell(sal_uInt16 nRow,sal_uInt16 nCell) const1915 const HTMLTableCell& HTMLTable::GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const
1916 {
1917 OSL_ENSURE(nRow < m_aRows.size(), "invalid row index in HTML table");
1918 return m_aRows[nRow].GetCell(nCell);
1919 }
1920
GetInheritedAdjust() const1921 SvxAdjust HTMLTable::GetInheritedAdjust() const
1922 {
1923 SvxAdjust eAdjust = (m_nCurrentColumn<m_nCols ? m_aColumns[m_nCurrentColumn].GetAdjust()
1924 : SvxAdjust::End );
1925 if( SvxAdjust::End==eAdjust )
1926 eAdjust = m_aRows[m_nCurrentRow].GetAdjust();
1927
1928 return eAdjust;
1929 }
1930
GetInheritedVertOri() const1931 sal_Int16 HTMLTable::GetInheritedVertOri() const
1932 {
1933 // text::VertOrientation::TOP is default!
1934 sal_Int16 eVOri = m_aRows[m_nCurrentRow].GetVertOri();
1935 if( text::VertOrientation::TOP==eVOri && m_nCurrentColumn<m_nCols )
1936 eVOri = m_aColumns[m_nCurrentColumn].GetVertOri();
1937 if( text::VertOrientation::TOP==eVOri )
1938 eVOri = m_eVertOrientation;
1939
1940 OSL_ENSURE( m_eVertOrientation != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
1941 return eVOri;
1942 }
1943
InsertCell(std::shared_ptr<HTMLTableCnts> const & rCnts,sal_uInt16 nRowSpan,sal_uInt16 nColSpan,sal_uInt16 nCellWidth,bool bRelWidth,sal_uInt16 nCellHeight,sal_Int16 eVertOrient,std::shared_ptr<SvxBrushItem> const & rBGBrushItem,std::shared_ptr<SvxBoxItem> const & rBoxItem,bool bHasNumFormat,sal_uInt32 nNumFormat,bool bHasValue,double nValue,bool bNoWrap)1944 void HTMLTable::InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts,
1945 sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
1946 sal_uInt16 nCellWidth, bool bRelWidth, sal_uInt16 nCellHeight,
1947 sal_Int16 eVertOrient, std::shared_ptr<SvxBrushItem> const& rBGBrushItem,
1948 std::shared_ptr<SvxBoxItem> const& rBoxItem,
1949 bool bHasNumFormat, sal_uInt32 nNumFormat,
1950 bool bHasValue, double nValue, bool bNoWrap )
1951 {
1952 if( !nRowSpan || static_cast<sal_uInt32>(m_nCurrentRow) + nRowSpan > USHRT_MAX )
1953 nRowSpan = 1;
1954
1955 if( !nColSpan || static_cast<sal_uInt32>(m_nCurrentColumn) + nColSpan > USHRT_MAX )
1956 nColSpan = 1;
1957
1958 sal_uInt16 nColsReq = m_nCurrentColumn + nColSpan;
1959 sal_uInt16 nRowsReq = m_nCurrentRow + nRowSpan;
1960 sal_uInt16 i, j;
1961
1962 // if we need more columns than we currently have, we need to add cells for all rows
1963 if( m_nCols < nColsReq )
1964 {
1965 m_aColumns.resize(nColsReq);
1966 for( i=0; i<m_nRows; i++ )
1967 m_aRows[i].Expand( nColsReq, i<m_nCurrentRow );
1968 m_nCols = nColsReq;
1969 OSL_ENSURE(m_aColumns.size() == m_nCols,
1970 "wrong number of columns after expanding");
1971 }
1972 if( nColsReq > m_nFilledColumns )
1973 m_nFilledColumns = nColsReq;
1974
1975 // if we need more rows than we currently have, we need to add cells
1976 if( m_nRows < nRowsReq )
1977 {
1978 for( i=m_nRows; i<nRowsReq; i++ )
1979 m_aRows.emplace_back(m_nCols);
1980 m_nRows = nRowsReq;
1981 OSL_ENSURE(m_nRows == m_aRows.size(), "wrong number of rows in Insert");
1982 }
1983
1984 // Check if we have an overlap and could remove that
1985 sal_uInt16 nSpanedCols = 0;
1986 if( m_nCurrentRow>0 )
1987 {
1988 HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
1989 for( i=m_nCurrentColumn; i<nColsReq; i++ )
1990 {
1991 HTMLTableCell& rCell = rCurRow.GetCell(i);
1992 if (rCell.GetContents())
1993 {
1994 // A cell from a row further above overlaps this one.
1995 // Content and colors are coming from that cell and can be overwritten
1996 // or deleted (content) or copied (color) by ProtectRowSpan
1997 nSpanedCols = i + rCell.GetColSpan();
1998 FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() );
1999 if (rCell.GetRowSpan() > nRowSpan)
2000 ProtectRowSpan( nRowsReq, i,
2001 rCell.GetRowSpan()-nRowSpan );
2002 }
2003 }
2004 for( i=nColsReq; i<nSpanedCols; i++ )
2005 {
2006 // These contents are anchored in the row above in any case
2007 HTMLTableCell& rCell = rCurRow.GetCell(i);
2008 FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() );
2009 ProtectRowSpan( m_nCurrentRow, i, rCell.GetRowSpan() );
2010 }
2011 }
2012
2013 // Fill the cells
2014 for( i=nColSpan; i>0; i-- )
2015 {
2016 for( j=nRowSpan; j>0; j-- )
2017 {
2018 const bool bCovered = i != nColSpan || j != nRowSpan;
2019 GetCell( nRowsReq-j, nColsReq-i )
2020 .Set( rCnts, j, i, eVertOrient, rBGBrushItem, rBoxItem,
2021 bHasNumFormat, nNumFormat, bHasValue, nValue, bNoWrap, bCovered );
2022 }
2023 }
2024
2025 Size aTwipSz( bRelWidth ? 0 : nCellWidth, nCellHeight );
2026 if( (aTwipSz.Width() || aTwipSz.Height()) && Application::GetDefaultDevice() )
2027 {
2028 aTwipSz = Application::GetDefaultDevice()
2029 ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) );
2030 }
2031
2032 // Only set width on the first cell!
2033 if( nCellWidth )
2034 {
2035 sal_uInt16 nTmp = bRelWidth ? nCellWidth : static_cast<sal_uInt16>(aTwipSz.Width());
2036 GetCell( m_nCurrentRow, m_nCurrentColumn ).SetWidth( nTmp, bRelWidth );
2037 }
2038
2039 // Remember height
2040 if( nCellHeight && 1==nRowSpan )
2041 {
2042 m_aRows[m_nCurrentRow].SetHeight(static_cast<sal_uInt16>(aTwipSz.Height()));
2043 }
2044
2045 // Set the column counter behind the new cells
2046 m_nCurrentColumn = nColsReq;
2047 if( nSpanedCols > m_nCurrentColumn )
2048 m_nCurrentColumn = nSpanedCols;
2049
2050 // and search for the next free cell
2051 while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
2052 m_nCurrentColumn++;
2053 }
2054
CloseSection(bool bHead)2055 inline void HTMLTable::CloseSection( bool bHead )
2056 {
2057 // Close the preceding sections if there's already a row
2058 OSL_ENSURE( m_nCurrentRow<=m_nRows, "invalid current row" );
2059 if( m_nCurrentRow>0 && m_nCurrentRow<=m_nRows )
2060 m_aRows[m_nCurrentRow-1].SetEndOfGroup();
2061 if( bHead )
2062 m_nHeadlineRepeat = m_nCurrentRow;
2063 }
2064
OpenRow(SvxAdjust eAdjust,sal_Int16 eVertOrient,std::unique_ptr<SvxBrushItem> & rBGBrushItem)2065 void HTMLTable::OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOrient,
2066 std::unique_ptr<SvxBrushItem>& rBGBrushItem)
2067 {
2068 sal_uInt16 nRowsReq = m_nCurrentRow+1;
2069
2070 // create the next row if it's not there already
2071 if( m_nRows<nRowsReq )
2072 {
2073 for( sal_uInt16 i=m_nRows; i<nRowsReq; i++ )
2074 m_aRows.emplace_back(m_nCols);
2075 m_nRows = nRowsReq;
2076 OSL_ENSURE( m_nRows == m_aRows.size(),
2077 "Row number in OpenRow is wrong" );
2078 }
2079
2080 HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
2081 rCurRow.SetAdjust(eAdjust);
2082 rCurRow.SetVertOri(eVertOrient);
2083 if (rBGBrushItem)
2084 m_aRows[m_nCurrentRow].SetBGBrush(rBGBrushItem);
2085
2086 // reset the column counter
2087 m_nCurrentColumn=0;
2088
2089 // and search for the next free cell
2090 while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
2091 m_nCurrentColumn++;
2092 }
2093
CloseRow(bool bEmpty)2094 void HTMLTable::CloseRow( bool bEmpty )
2095 {
2096 OSL_ENSURE( m_nCurrentRow<m_nRows, "current row after table end" );
2097
2098 // empty cells just get a slightly thicker lower border!
2099 if( bEmpty )
2100 {
2101 if( m_nCurrentRow > 0 )
2102 m_aRows[m_nCurrentRow-1].IncEmptyRows();
2103 return;
2104 }
2105
2106 HTMLTableRow& rRow = m_aRows[m_nCurrentRow];
2107
2108 // modify the COLSPAN of all empty cells at the row end in a way, that they're forming a single cell
2109 // that can be done here (and not earlier) since there's no more cells in that row
2110 sal_uInt16 i=m_nCols;
2111 while( i )
2112 {
2113 HTMLTableCell& rCell = rRow.GetCell(--i);
2114 if (!rCell.GetContents())
2115 {
2116 sal_uInt16 nColSpan = m_nCols-i;
2117 if( nColSpan > 1 )
2118 rCell.SetColSpan(nColSpan);
2119 }
2120 else
2121 break;
2122 }
2123
2124 m_nCurrentRow++;
2125 }
2126
CloseColGroup(sal_uInt16 nSpan,sal_uInt16 _nWidth,bool bRelWidth,SvxAdjust eAdjust,sal_Int16 eVertOrient)2127 inline void HTMLTable::CloseColGroup( sal_uInt16 nSpan, sal_uInt16 _nWidth,
2128 bool bRelWidth, SvxAdjust eAdjust,
2129 sal_Int16 eVertOrient )
2130 {
2131 if( nSpan )
2132 InsertCol( nSpan, _nWidth, bRelWidth, eAdjust, eVertOrient );
2133
2134 OSL_ENSURE( m_nCurrentColumn<=m_nCols, "invalid column" );
2135 if( m_nCurrentColumn>0 && m_nCurrentColumn<=m_nCols )
2136 m_aColumns[m_nCurrentColumn-1].SetEndOfGroup();
2137 }
2138
InsertCol(sal_uInt16 nSpan,sal_uInt16 nColWidth,bool bRelWidth,SvxAdjust eAdjust,sal_Int16 eVertOrient)2139 void HTMLTable::InsertCol( sal_uInt16 nSpan, sal_uInt16 nColWidth, bool bRelWidth,
2140 SvxAdjust eAdjust, sal_Int16 eVertOrient )
2141 {
2142 // #i35143# - no columns, if rows already exist.
2143 if ( m_nRows > 0 )
2144 return;
2145
2146 sal_uInt16 i;
2147
2148 if( !nSpan )
2149 nSpan = 1;
2150
2151 sal_uInt16 nColsReq = m_nCurrentColumn + nSpan;
2152
2153 if( m_nCols < nColsReq )
2154 {
2155 m_aColumns.resize(nColsReq);
2156 m_nCols = nColsReq;
2157 }
2158
2159 Size aTwipSz( bRelWidth ? 0 : nColWidth, 0 );
2160 if( aTwipSz.Width() && Application::GetDefaultDevice() )
2161 {
2162 aTwipSz = Application::GetDefaultDevice()
2163 ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) );
2164 }
2165
2166 for( i=m_nCurrentColumn; i<nColsReq; i++ )
2167 {
2168 HTMLTableColumn& rCol = m_aColumns[i];
2169 sal_uInt16 nTmp = bRelWidth ? nColWidth : static_cast<sal_uInt16>(aTwipSz.Width());
2170 rCol.SetWidth( nTmp, bRelWidth );
2171 rCol.SetAdjust( eAdjust );
2172 rCol.SetVertOri( eVertOrient );
2173 }
2174
2175 m_bColSpec = true;
2176
2177 m_nCurrentColumn = nColsReq;
2178 }
2179
CloseTable()2180 void HTMLTable::CloseTable()
2181 {
2182 sal_uInt16 i;
2183
2184 // The number of table rows is only dependent on the <TR> elements (i.e. nCurRow).
2185 // Rows that are spanned via ROWSPAN behind nCurRow need to be deleted
2186 // and we need to adjust the ROWSPAN in the rows above
2187 if( m_nRows>m_nCurrentRow )
2188 {
2189 HTMLTableRow& rPrevRow = m_aRows[m_nCurrentRow-1];
2190 for( i=0; i<m_nCols; i++ )
2191 {
2192 HTMLTableCell& rCell = rPrevRow.GetCell(i);
2193 if (rCell.GetRowSpan() > 1)
2194 {
2195 FixRowSpan(m_nCurrentRow-1, i, rCell.GetContents().get());
2196 ProtectRowSpan(m_nCurrentRow, i, m_aRows[m_nCurrentRow].GetCell(i).GetRowSpan());
2197 }
2198 }
2199 for( i=m_nRows-1; i>=m_nCurrentRow; i-- )
2200 m_aRows.erase(m_aRows.begin() + i);
2201 m_nRows = m_nCurrentRow;
2202 }
2203
2204 // if the table has no column, we need to add one
2205 if( 0==m_nCols )
2206 {
2207 m_aColumns.resize(1);
2208 for( i=0; i<m_nRows; i++ )
2209 m_aRows[i].Expand(1);
2210 m_nCols = 1;
2211 m_nFilledColumns = 1;
2212 }
2213
2214 // if the table has no row, we need to add one
2215 if( 0==m_nRows )
2216 {
2217 m_aRows.emplace_back(m_nCols);
2218 m_nRows = 1;
2219 m_nCurrentRow = 1;
2220 }
2221
2222 if( m_nFilledColumns < m_nCols )
2223 {
2224 m_aColumns.erase(m_aColumns.begin() + m_nFilledColumns, m_aColumns.begin() + m_nCols);
2225 for( i=0; i<m_nRows; i++ )
2226 m_aRows[i].Shrink( m_nFilledColumns );
2227 m_nCols = m_nFilledColumns;
2228 }
2229 }
2230
MakeTable_(SwTableBox * pBox)2231 void HTMLTable::MakeTable_( SwTableBox *pBox )
2232 {
2233 SwTableLines& rLines = (pBox ? pBox->GetTabLines()
2234 : const_cast<SwTable *>(m_pSwTable)->GetTabLines() );
2235
2236 for( sal_uInt16 i=0; i<m_nRows; i++ )
2237 {
2238 SwTableLine *pLine = MakeTableLine( pBox, i, 0, i+1, m_nCols );
2239 if( pBox || i > 0 )
2240 rLines.push_back( pLine );
2241 }
2242 }
2243
2244 /* How are tables aligned?
2245
2246 first row: without paragraph indents
2247 second row: with paragraph indents
2248
2249 ALIGN= LEFT RIGHT CENTER -
2250 -------------------------------------------------------------------------
2251 xxx for tables with WIDTH=nn% the percentage value is important:
2252 xxx nn = 100 text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL %
2253 xxx text::HoriOrientation::NONE text::HoriOrientation::NONE text::HoriOrientation::NONE % text::HoriOrientation::NONE %
2254 xxx nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
2255 xxx frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE %
2256
2257 for tables with WIDTH=nn% the percentage value is important:
2258 nn = 100 text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
2259 text::HoriOrientation::LEFT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT_AND %
2260 nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
2261 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE %
2262
2263 otherwise the calculated width w
2264 w = avail* text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT
2265 HORI_LEDT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT_AND
2266 w < avail frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::LEFT
2267 frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::NONE
2268
2269 xxx *) if for the table no size was specified, always
2270 xxx text::HoriOrientation::FULL is taken
2271
2272 */
2273
MakeTable(SwTableBox * pBox,sal_uInt16 nAbsAvail,sal_uInt16 nRelAvail,sal_uInt16 nAbsLeftSpace,sal_uInt16 nAbsRightSpace,sal_uInt16 nInhAbsSpace)2274 void HTMLTable::MakeTable( SwTableBox *pBox, sal_uInt16 nAbsAvail,
2275 sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
2276 sal_uInt16 nAbsRightSpace, sal_uInt16 nInhAbsSpace )
2277 {
2278 OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
2279 "Was CloseTable not called?" );
2280
2281 OSL_ENSURE(m_xLayoutInfo == nullptr, "Table already has layout info");
2282
2283 // Calculate borders of the table and all contained tables
2284 SetBorders();
2285
2286 // Step 1: needed layout structures are created (including tables in tables)
2287 CreateLayoutInfo();
2288
2289 // Step 2: the minimal and maximal column width is calculated
2290 // (including tables in tables). Since we don't have boxes yet,
2291 // we'll work on the start nodes
2292 m_xLayoutInfo->AutoLayoutPass1();
2293
2294 // Step 3: the actual column widths of this table are calculated (not tables in tables)
2295 // We need this now to decide if we need filler cells
2296 // (Pass1 was needed because of this as well)
2297 m_xLayoutInfo->AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace,
2298 nAbsRightSpace, nInhAbsSpace );
2299
2300 // Set adjustment for the top table
2301 sal_Int16 eHoriOri;
2302 if (m_bForceFrame)
2303 {
2304 // The table should go in a text frame and it's narrower than the
2305 // available space and not 100% wide. So it gets a border
2306 eHoriOri = m_bPrcWidth ? text::HoriOrientation::FULL : text::HoriOrientation::LEFT;
2307 }
2308 else switch (m_eTableAdjust)
2309 {
2310 // The table either fits the page but shouldn't get a text frame,
2311 // or it's wider than the page so it doesn't need a text frame
2312
2313 case SvxAdjust::Right:
2314 // Don't be considerate of the right margin in right-adjusted tables
2315 eHoriOri = text::HoriOrientation::RIGHT;
2316 break;
2317 case SvxAdjust::Center:
2318 // Centred tables are not considerate of margins
2319 eHoriOri = text::HoriOrientation::CENTER;
2320 break;
2321 case SvxAdjust::Left:
2322 default:
2323 // left-adjusted tables are only considerate of the left margin
2324 eHoriOri = m_nLeftMargin ? text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::LEFT;
2325 break;
2326 }
2327
2328 if (!m_pSwTable)
2329 {
2330 SAL_WARN("sw.html", "no table");
2331 return;
2332 }
2333
2334 // get the table format and adapt it
2335 SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
2336 pFrameFormat->SetFormatAttr( SwFormatHoriOrient(0, eHoriOri) );
2337 if (text::HoriOrientation::LEFT_AND_WIDTH == eHoriOri)
2338 {
2339 OSL_ENSURE( m_nLeftMargin || m_nRightMargin,
2340 "There are still leftovers from relative margins" );
2341
2342 // The right margin will be ignored anyway.
2343 SvxLRSpaceItem aLRItem( m_pSwTable->GetFrameFormat()->GetLRSpace() );
2344 aLRItem.SetLeft( m_nLeftMargin );
2345 aLRItem.SetRight( m_nRightMargin );
2346 pFrameFormat->SetFormatAttr( aLRItem );
2347 }
2348
2349 if (m_bPrcWidth && text::HoriOrientation::FULL != eHoriOri)
2350 {
2351 pFrameFormat->LockModify();
2352 SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
2353 aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidth) );
2354 pFrameFormat->SetFormatAttr( aFrameSize );
2355 pFrameFormat->UnlockModify();
2356 }
2357
2358 // get the default line and box format
2359 // remember the first box and unlist it from the first row
2360 SwTableLine *pLine1 = (m_pSwTable->GetTabLines())[0];
2361 m_xBox1.reset((pLine1->GetTabBoxes())[0]);
2362 pLine1->GetTabBoxes().erase(pLine1->GetTabBoxes().begin());
2363
2364 m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat());
2365 m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_xBox1->GetFrameFormat());
2366
2367 MakeTable_( pBox );
2368
2369 // Finally, we'll do a garbage collection for the top level table
2370
2371 if( 1==m_nRows && m_nHeight && 1==m_pSwTable->GetTabLines().size() )
2372 {
2373 // Set height of a one-row table as the minimum width of the row
2374 // Was originally a fixed height, but that made problems
2375 // and is not Netscape 4.0 compliant
2376 m_nHeight = SwHTMLParser::ToTwips( m_nHeight );
2377 if( m_nHeight < MINLAY )
2378 m_nHeight = MINLAY;
2379
2380 (m_pSwTable->GetTabLines())[0]->ClaimFrameFormat();
2381 (m_pSwTable->GetTabLines())[0]->GetFrameFormat()
2382 ->SetFormatAttr( SwFormatFrameSize( ATT_MIN_SIZE, 0, m_nHeight ) );
2383 }
2384
2385 if( GetBGBrush() )
2386 m_pSwTable->GetFrameFormat()->SetFormatAttr( *GetBGBrush() );
2387
2388 const_cast<SwTable *>(m_pSwTable)->SetRowsToRepeat( static_cast< sal_uInt16 >(m_nHeadlineRepeat) );
2389 const_cast<SwTable *>(m_pSwTable)->GCLines();
2390
2391 bool bIsInFlyFrame = m_pContext && m_pContext->GetFrameFormat();
2392 if( bIsInFlyFrame && !m_nWidth )
2393 {
2394 SvxAdjust eAdjust = GetTableAdjust(false);
2395 if (eAdjust != SvxAdjust::Left &&
2396 eAdjust != SvxAdjust::Right)
2397 {
2398 // If a table with a width attribute isn't flowed around left or right
2399 // we'll stack it with a border of 100% width, so its size will
2400 // be adapted. That text frame mustn't be modified
2401 OSL_ENSURE( HasToFly(), "Why is the table in a frame?" );
2402 sal_uInt32 nMin = m_xLayoutInfo->GetMin();
2403 if( nMin > USHRT_MAX )
2404 nMin = USHRT_MAX;
2405 SwFormatFrameSize aFlyFrameSize( ATT_VAR_SIZE, static_cast<SwTwips>(nMin), MINLAY );
2406 aFlyFrameSize.SetWidthPercent( 100 );
2407 m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize );
2408 bIsInFlyFrame = false;
2409 }
2410 else
2411 {
2412 // left or right adjusted table without width mustn't be adjusted in width
2413 // as they would only shrink but never grow
2414 m_xLayoutInfo->SetMustNotRecalc( true );
2415 if( m_pContext->GetFrameFormat()->GetAnchor().GetContentAnchor()
2416 ->nNode.GetNode().FindTableNode() )
2417 {
2418 sal_uInt32 nMax = m_xLayoutInfo->GetMax();
2419 if( nMax > USHRT_MAX )
2420 nMax = USHRT_MAX;
2421 SwFormatFrameSize aFlyFrameSize( ATT_VAR_SIZE, static_cast<SwTwips>(nMax), MINLAY );
2422 m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize );
2423 bIsInFlyFrame = false;
2424 }
2425 else
2426 {
2427 m_xLayoutInfo->SetMustNotResize( true );
2428 }
2429 }
2430 }
2431 m_xLayoutInfo->SetMayBeInFlyFrame( bIsInFlyFrame );
2432
2433 // Only tables with relative width or without width should be modified
2434 m_xLayoutInfo->SetMustResize( m_bPrcWidth || !m_nWidth );
2435
2436 if (!pLine1->GetTabBoxes().empty())
2437 m_xLayoutInfo->SetWidths();
2438 else
2439 SAL_WARN("sw.html", "no table box");
2440
2441 const_cast<SwTable *>(m_pSwTable)->SetHTMLTableLayout(m_xLayoutInfo);
2442
2443 if( m_pResizeDrawObjects )
2444 {
2445 sal_uInt16 nCount = m_pResizeDrawObjects->size();
2446 for( sal_uInt16 i=0; i<nCount; i++ )
2447 {
2448 SdrObject *pObj = (*m_pResizeDrawObjects)[i];
2449 sal_uInt16 nRow = (*m_pDrawObjectPrcWidths)[3*i];
2450 sal_uInt16 nCol = (*m_pDrawObjectPrcWidths)[3*i+1];
2451 sal_uInt8 nPrcWidth = static_cast<sal_uInt8>((*m_pDrawObjectPrcWidths)[3*i+2]);
2452
2453 SwHTMLTableLayoutCell *pLayoutCell =
2454 m_xLayoutInfo->GetCell( nRow, nCol );
2455 sal_uInt16 nColSpan = pLayoutCell->GetColSpan();
2456
2457 sal_uInt16 nWidth2, nDummy;
2458 m_xLayoutInfo->GetAvail( nCol, nColSpan, nWidth2, nDummy );
2459 nWidth2 = static_cast< sal_uInt16 >((static_cast<long>(m_nWidth) * nPrcWidth) / 100);
2460
2461 SwHTMLParser::ResizeDrawObject( pObj, nWidth2 );
2462 }
2463 }
2464
2465 }
2466
SetTable(const SwStartNode * pStNd,std::unique_ptr<HTMLTableContext> pCntxt,sal_uInt16 nLeft,sal_uInt16 nRight,const SwTable * pSwTab,bool bFrcFrame)2467 void HTMLTable::SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt,
2468 sal_uInt16 nLeft, sal_uInt16 nRight,
2469 const SwTable *pSwTab, bool bFrcFrame )
2470 {
2471 m_pPrevStartNode = pStNd;
2472 m_pSwTable = pSwTab;
2473 m_pContext = std::move(pCntxt);
2474
2475 m_nLeftMargin = nLeft;
2476 m_nRightMargin = nRight;
2477
2478 m_bForceFrame = bFrcFrame;
2479 }
2480
RegisterDrawObject(SdrObject * pObj,sal_uInt8 nPrcWidth)2481 void HTMLTable::RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPrcWidth )
2482 {
2483 if( !m_pResizeDrawObjects )
2484 m_pResizeDrawObjects.reset(new SdrObjects);
2485 m_pResizeDrawObjects->push_back( pObj );
2486
2487 if( !m_pDrawObjectPrcWidths )
2488 m_pDrawObjectPrcWidths.reset(new std::vector<sal_uInt16>);
2489 m_pDrawObjectPrcWidths->push_back( m_nCurrentRow );
2490 m_pDrawObjectPrcWidths->push_back( m_nCurrentColumn );
2491 m_pDrawObjectPrcWidths->push_back( static_cast<sal_uInt16>(nPrcWidth) );
2492 }
2493
MakeParentContents()2494 void HTMLTable::MakeParentContents()
2495 {
2496 if( !GetContext() && !HasParentSection() )
2497 {
2498 SetParentContents(
2499 m_pParser->InsertTableContents( GetIsParentHeader() ) );
2500
2501 SetHasParentSection( true );
2502 }
2503 }
2504
SavePREListingXMP(SwHTMLParser & rParser)2505 void HTMLTableContext::SavePREListingXMP( SwHTMLParser& rParser )
2506 {
2507 bRestartPRE = rParser.IsReadPRE();
2508 bRestartXMP = rParser.IsReadXMP();
2509 bRestartListing = rParser.IsReadListing();
2510 rParser.FinishPREListingXMP();
2511 }
2512
RestorePREListingXMP(SwHTMLParser & rParser)2513 void HTMLTableContext::RestorePREListingXMP( SwHTMLParser& rParser )
2514 {
2515 rParser.FinishPREListingXMP();
2516
2517 if( bRestartPRE )
2518 rParser.StartPRE();
2519
2520 if( bRestartXMP )
2521 rParser.StartXMP();
2522
2523 if( bRestartListing )
2524 rParser.StartListing();
2525 }
2526
InsertTableSection(const SwStartNode * pPrevStNd)2527 const SwStartNode *SwHTMLParser::InsertTableSection
2528 ( const SwStartNode *pPrevStNd )
2529 {
2530 OSL_ENSURE( pPrevStNd, "Start-Node is NULL" );
2531
2532 m_pCSS1Parser->SetTDTagStyles();
2533 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TABLE );
2534
2535 const SwStartNode *pStNd;
2536 if (m_xTable->m_bFirstCell )
2537 {
2538 SwNode *const pNd = & m_pPam->GetPoint()->nNode.GetNode();
2539 pNd->GetTextNode()->ChgFormatColl( pColl );
2540 pStNd = pNd->FindTableBoxStartNode();
2541 m_xTable->m_bFirstCell = false;
2542 }
2543 else if (pPrevStNd)
2544 {
2545 const SwNode* pNd;
2546 if( pPrevStNd->IsTableNode() )
2547 pNd = pPrevStNd;
2548 else
2549 pNd = pPrevStNd->EndOfSectionNode();
2550 SwNodeIndex nIdx( *pNd, 1 );
2551 pStNd = m_xDoc->GetNodes().MakeTextSection( nIdx, SwTableBoxStartNode,
2552 pColl );
2553 m_xTable->IncBoxCount();
2554 }
2555 else
2556 {
2557 eState = SvParserState::Error;
2558 return nullptr;
2559 }
2560
2561 //Added defaults to CJK and CTL
2562 SwContentNode *pCNd = m_xDoc->GetNodes()[pStNd->GetIndex()+1] ->GetContentNode();
2563 SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
2564 pCNd->SetAttr( aFontHeight );
2565 SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
2566 pCNd->SetAttr( aFontHeightCJK );
2567 SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
2568 pCNd->SetAttr( aFontHeightCTL );
2569
2570 return pStNd;
2571 }
2572
InsertTableSection(sal_uInt16 nPoolId)2573 const SwStartNode *SwHTMLParser::InsertTableSection( sal_uInt16 nPoolId )
2574 {
2575 switch( nPoolId )
2576 {
2577 case RES_POOLCOLL_TABLE_HDLN:
2578 m_pCSS1Parser->SetTHTagStyles();
2579 break;
2580 case RES_POOLCOLL_TABLE:
2581 m_pCSS1Parser->SetTDTagStyles();
2582 break;
2583 }
2584
2585 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( nPoolId );
2586
2587 SwNode *const pNd = & m_pPam->GetPoint()->nNode.GetNode();
2588 const SwStartNode *pStNd;
2589 if (m_xTable->m_bFirstCell)
2590 {
2591 SwTextNode* pTextNd = pNd->GetTextNode();
2592 if (!pTextNd)
2593 {
2594 eState = SvParserState::Error;
2595 return nullptr;
2596 }
2597 pTextNd->ChgFormatColl(pColl);
2598 m_xTable->m_bFirstCell = false;
2599 pStNd = pNd->FindTableBoxStartNode();
2600 }
2601 else
2602 {
2603 SwTableNode *pTableNd = pNd->FindTableNode();
2604 if (!pTableNd)
2605 {
2606 eState = SvParserState::Error;
2607 return nullptr;
2608 }
2609 if( pTableNd->GetTable().GetHTMLTableLayout() )
2610 { // if there is already a HTMTableLayout, this table is already finished
2611 // and we have to look for the right table in the environment
2612 SwTableNode *pOutTable = pTableNd;
2613 do {
2614 pTableNd = pOutTable;
2615 pOutTable = pOutTable->StartOfSectionNode()->FindTableNode();
2616 } while( pOutTable && pTableNd->GetTable().GetHTMLTableLayout() );
2617 }
2618 SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
2619 pStNd = m_xDoc->GetNodes().MakeTextSection( aIdx, SwTableBoxStartNode,
2620 pColl );
2621
2622 m_pPam->GetPoint()->nNode = pStNd->GetIndex() + 1;
2623 SwTextNode *pTextNd = m_pPam->GetPoint()->nNode.GetNode().GetTextNode();
2624 m_pPam->GetPoint()->nContent.Assign( pTextNd, 0 );
2625 m_xTable->IncBoxCount();
2626 }
2627
2628 if (!pStNd)
2629 {
2630 eState = SvParserState::Error;
2631 }
2632
2633 return pStNd;
2634 }
2635
InsertTempTableCaptionSection()2636 SwStartNode *SwHTMLParser::InsertTempTableCaptionSection()
2637 {
2638 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TEXT );
2639 SwNodeIndex& rIdx = m_pPam->GetPoint()->nNode;
2640 rIdx = m_xDoc->GetNodes().GetEndOfExtras();
2641 SwStartNode *pStNd = m_xDoc->GetNodes().MakeTextSection( rIdx,
2642 SwNormalStartNode, pColl );
2643
2644 rIdx = pStNd->GetIndex() + 1;
2645 m_pPam->GetPoint()->nContent.Assign( rIdx.GetNode().GetTextNode(), 0 );
2646
2647 return pStNd;
2648 }
2649
StripTrailingLF()2650 sal_Int32 SwHTMLParser::StripTrailingLF()
2651 {
2652 sal_Int32 nStripped = 0;
2653
2654 const sal_Int32 nLen = m_pPam->GetPoint()->nContent.GetIndex();
2655 if( nLen )
2656 {
2657 SwTextNode* pTextNd = m_pPam->GetPoint()->nNode.GetNode().GetTextNode();
2658 // careful, when comments aren't ignored!!!
2659 if( pTextNd )
2660 {
2661 sal_Int32 nPos = nLen;
2662 sal_Int32 nLFCount = 0;
2663 while (nPos && ('\x0a' == pTextNd->GetText()[--nPos]))
2664 nLFCount++;
2665
2666 if( nLFCount )
2667 {
2668 if( nLFCount > 2 )
2669 {
2670 // On Netscape, a paragraph end matches 2 LFs
2671 // (1 is just a newline, 2 creates a blank line)
2672 // We already have this space with the lower paragraph gap
2673 // If there's a paragraph after the <BR>, we take the maximum
2674 // of the gap that results from the <BR> and <P>
2675 // That's why we need to delete 2 respectively all if less than 2
2676 nLFCount = 2;
2677 }
2678
2679 nPos = nLen - nLFCount;
2680 SwIndex nIdx( pTextNd, nPos );
2681 pTextNd->EraseText( nIdx, nLFCount );
2682 nStripped = nLFCount;
2683 }
2684 }
2685 }
2686
2687 return nStripped;
2688 }
2689
CreateBrushItem(const Color * pColor,const OUString & rImageURL,const OUString & rStyle,const OUString & rId,const OUString & rClass)2690 SvxBrushItem* SwHTMLParser::CreateBrushItem( const Color *pColor,
2691 const OUString& rImageURL,
2692 const OUString& rStyle,
2693 const OUString& rId,
2694 const OUString& rClass )
2695 {
2696 SvxBrushItem *pBrushItem = nullptr;
2697
2698 if( !rStyle.isEmpty() || !rId.isEmpty() || !rClass.isEmpty() )
2699 {
2700 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), svl::Items<RES_BACKGROUND,
2701 RES_BACKGROUND>{} );
2702 SvxCSS1PropertyInfo aPropInfo;
2703
2704 if( !rClass.isEmpty() )
2705 {
2706 OUString aClass( rClass );
2707 SwCSS1Parser::GetScriptFromClass( aClass );
2708 const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass );
2709 if( pClass )
2710 aItemSet.Put( pClass->GetItemSet() );
2711 }
2712
2713 if( !rId.isEmpty() )
2714 {
2715 const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId );
2716 if( pId )
2717 aItemSet.Put( pId->GetItemSet() );
2718 }
2719
2720 m_pCSS1Parser->ParseStyleOption( rStyle, aItemSet, aPropInfo );
2721 const SfxPoolItem *pItem = nullptr;
2722 if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false,
2723 &pItem ) )
2724 {
2725 pBrushItem = new SvxBrushItem( *static_cast<const SvxBrushItem *>(pItem) );
2726 }
2727 }
2728
2729 if( !pBrushItem && (pColor || !rImageURL.isEmpty()) )
2730 {
2731 pBrushItem = new SvxBrushItem(RES_BACKGROUND);
2732
2733 if( pColor )
2734 pBrushItem->SetColor(*pColor);
2735
2736 if( !rImageURL.isEmpty() )
2737 {
2738 pBrushItem->SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), rImageURL, Link<OUString *, bool>(), false) );
2739 pBrushItem->SetGraphicPos( GPOS_TILED );
2740 }
2741 }
2742
2743 return pBrushItem;
2744 }
2745
2746 class SectionSaveStruct : public SwPendingData
2747 {
2748 sal_uInt16 m_nBaseFontStMinSave, m_nFontStMinSave, m_nFontStHeadStartSave;
2749 sal_uInt16 m_nDefListDeepSave;
2750 size_t m_nContextStMinSave;
2751 size_t m_nContextStAttrMinSave;
2752
2753 public:
2754
2755 std::shared_ptr<HTMLTable> m_xTable;
2756
2757 explicit SectionSaveStruct( SwHTMLParser& rParser );
2758
2759 #if OSL_DEBUG_LEVEL > 0
GetContextStAttrMin() const2760 size_t GetContextStAttrMin() const { return m_nContextStAttrMinSave; }
2761 #endif
2762 void Restore( SwHTMLParser& rParser );
2763 };
2764
SectionSaveStruct(SwHTMLParser & rParser)2765 SectionSaveStruct::SectionSaveStruct( SwHTMLParser& rParser ) :
2766 m_nBaseFontStMinSave(0), m_nFontStMinSave(0), m_nFontStHeadStartSave(0),
2767 m_nDefListDeepSave(0), m_nContextStMinSave(0), m_nContextStAttrMinSave(0)
2768 {
2769 // Freeze font stacks
2770 m_nBaseFontStMinSave = rParser.m_nBaseFontStMin;
2771 rParser.m_nBaseFontStMin = rParser.m_aBaseFontStack.size();
2772
2773 m_nFontStMinSave = rParser.m_nFontStMin;
2774 m_nFontStHeadStartSave = rParser.m_nFontStHeadStart;
2775 rParser.m_nFontStMin = rParser.m_aFontStack.size();
2776
2777 // Freeze context stack
2778 m_nContextStMinSave = rParser.m_nContextStMin;
2779 m_nContextStAttrMinSave = rParser.m_nContextStAttrMin;
2780 rParser.m_nContextStMin = rParser.m_aContexts.size();
2781 rParser.m_nContextStAttrMin = rParser.m_nContextStMin;
2782
2783 // And remember a few counters
2784 m_nDefListDeepSave = rParser.m_nDefListDeep;
2785 rParser.m_nDefListDeep = 0;
2786 }
2787
Restore(SwHTMLParser & rParser)2788 void SectionSaveStruct::Restore( SwHTMLParser& rParser )
2789 {
2790 // Unfreeze font stacks
2791 sal_uInt16 nMin = rParser.m_nBaseFontStMin;
2792 if( rParser.m_aBaseFontStack.size() > nMin )
2793 rParser.m_aBaseFontStack.erase( rParser.m_aBaseFontStack.begin() + nMin,
2794 rParser.m_aBaseFontStack.end() );
2795 rParser.m_nBaseFontStMin = m_nBaseFontStMinSave;
2796
2797 nMin = rParser.m_nFontStMin;
2798 if( rParser.m_aFontStack.size() > nMin )
2799 rParser.m_aFontStack.erase( rParser.m_aFontStack.begin() + nMin,
2800 rParser.m_aFontStack.end() );
2801 rParser.m_nFontStMin = m_nFontStMinSave;
2802 rParser.m_nFontStHeadStart = m_nFontStHeadStartSave;
2803
2804 OSL_ENSURE( rParser.m_aContexts.size() == rParser.m_nContextStMin &&
2805 rParser.m_aContexts.size() == rParser.m_nContextStAttrMin,
2806 "The Context Stack was not cleaned up" );
2807 rParser.m_nContextStMin = m_nContextStMinSave;
2808 rParser.m_nContextStAttrMin = m_nContextStAttrMinSave;
2809
2810 // Reconstruct a few counters
2811 rParser.m_nDefListDeep = m_nDefListDeepSave;
2812
2813 // Reset a few flags
2814 rParser.m_bNoParSpace = false;
2815 rParser.m_nOpenParaToken = HtmlTokenId::NONE;
2816
2817 rParser.m_aParaAttrs.clear();
2818 }
2819
2820 class CellSaveStruct : public SectionSaveStruct
2821 {
2822 OUString m_aStyle, m_aId, m_aClass;
2823 OUString m_aBGImage;
2824 Color m_aBGColor;
2825 std::shared_ptr<SvxBoxItem> m_xBoxItem;
2826
2827 std::shared_ptr<HTMLTableCnts> m_xCnts; // List of all contents
2828 HTMLTableCnts* m_pCurrCnts; // current content or 0
2829 std::unique_ptr<SwNodeIndex> m_pNoBreakEndNodeIndex; // Paragraph index of a <NOBR>
2830
2831 double m_nValue;
2832
2833 sal_uInt32 m_nNumFormat;
2834
2835 sal_uInt16 m_nRowSpan, m_nColSpan, m_nWidth, m_nHeight;
2836 sal_Int32 m_nNoBreakEndContentPos; // Character index of a <NOBR>
2837
2838 sal_Int16 m_eVertOri;
2839
2840 bool const m_bHead : 1;
2841 bool m_bPrcWidth : 1;
2842 bool m_bHasNumFormat : 1;
2843 bool m_bHasValue : 1;
2844 bool m_bBGColor : 1;
2845 bool m_bNoWrap : 1; // NOWRAP option
2846 bool m_bNoBreak : 1; // NOBREAK tag
2847
2848 public:
2849
2850 CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable, bool bHd,
2851 bool bReadOpt );
2852
2853 void AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts );
HasFirstContents() const2854 bool HasFirstContents() const { return m_xCnts.get(); }
2855
ClearIsInSection()2856 void ClearIsInSection() { m_pCurrCnts = nullptr; }
IsInSection() const2857 bool IsInSection() const { return m_pCurrCnts!=nullptr; }
2858
2859 void InsertCell( SwHTMLParser& rParser, HTMLTable *pCurTable );
2860
IsHeaderCell() const2861 bool IsHeaderCell() const { return m_bHead; }
2862
2863 void StartNoBreak( const SwPosition& rPos );
2864 void EndNoBreak( const SwPosition& rPos );
2865 void CheckNoBreak( const SwPosition& rPos );
2866 };
2867
CellSaveStruct(SwHTMLParser & rParser,HTMLTable const * pCurTable,bool bHd,bool bReadOpt)2868 CellSaveStruct::CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable,
2869 bool bHd, bool bReadOpt ) :
2870 SectionSaveStruct( rParser ),
2871 m_pCurrCnts( nullptr ),
2872 m_nValue( 0.0 ),
2873 m_nNumFormat( 0 ),
2874 m_nRowSpan( 1 ),
2875 m_nColSpan( 1 ),
2876 m_nWidth( 0 ),
2877 m_nHeight( 0 ),
2878 m_nNoBreakEndContentPos( 0 ),
2879 m_eVertOri( pCurTable->GetInheritedVertOri() ),
2880 m_bHead( bHd ),
2881 m_bPrcWidth( false ),
2882 m_bHasNumFormat( false ),
2883 m_bHasValue( false ),
2884 m_bBGColor( false ),
2885 m_bNoWrap( false ),
2886 m_bNoBreak( false )
2887 {
2888 OUString aNumFormat, aValue, aDir, aLang;
2889 SvxAdjust eAdjust( pCurTable->GetInheritedAdjust() );
2890
2891 if( bReadOpt )
2892 {
2893 const HTMLOptions& rOptions = rParser.GetOptions();
2894 for (size_t i = rOptions.size(); i; )
2895 {
2896 const HTMLOption& rOption = rOptions[--i];
2897 switch( rOption.GetToken() )
2898 {
2899 case HtmlOptionId::ID:
2900 m_aId = rOption.GetString();
2901 break;
2902 case HtmlOptionId::COLSPAN:
2903 m_nColSpan = static_cast<sal_uInt16>(rOption.GetNumber());
2904 if (m_nColSpan > 256)
2905 {
2906 SAL_INFO("sw.html", "ignoring huge COLSPAN " << m_nColSpan);
2907 m_nColSpan = 1;
2908 }
2909 break;
2910 case HtmlOptionId::ROWSPAN:
2911 m_nRowSpan = static_cast<sal_uInt16>(rOption.GetNumber());
2912 if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing()))
2913 {
2914 SAL_INFO("sw.html", "ignoring huge ROWSPAN " << m_nRowSpan);
2915 m_nRowSpan = 1;
2916 }
2917 break;
2918 case HtmlOptionId::ALIGN:
2919 eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
2920 break;
2921 case HtmlOptionId::VALIGN:
2922 m_eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, m_eVertOri );
2923 break;
2924 case HtmlOptionId::WIDTH:
2925 m_nWidth = static_cast<sal_uInt16>(rOption.GetNumber()); // Just for Netscape
2926 m_bPrcWidth = (rOption.GetString().indexOf('%') != -1);
2927 if( m_bPrcWidth && m_nWidth>100 )
2928 m_nWidth = 100;
2929 break;
2930 case HtmlOptionId::HEIGHT:
2931 m_nHeight = static_cast<sal_uInt16>(rOption.GetNumber()); // Just for Netscape
2932 if( rOption.GetString().indexOf('%') != -1)
2933 m_nHeight = 0; // don't consider % attributes
2934 break;
2935 case HtmlOptionId::BGCOLOR:
2936 // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape
2937 // *really* not on other tags
2938 if( !rOption.GetString().isEmpty() )
2939 {
2940 rOption.GetColor( m_aBGColor );
2941 m_bBGColor = true;
2942 }
2943 break;
2944 case HtmlOptionId::BACKGROUND:
2945 m_aBGImage = rOption.GetString();
2946 break;
2947 case HtmlOptionId::STYLE:
2948 m_aStyle = rOption.GetString();
2949 break;
2950 case HtmlOptionId::CLASS:
2951 m_aClass = rOption.GetString();
2952 break;
2953 case HtmlOptionId::LANG:
2954 aLang = rOption.GetString();
2955 break;
2956 case HtmlOptionId::DIR:
2957 aDir = rOption.GetString();
2958 break;
2959 case HtmlOptionId::SDNUM:
2960 aNumFormat = rOption.GetString();
2961 m_bHasNumFormat = true;
2962 break;
2963 case HtmlOptionId::SDVAL:
2964 m_bHasValue = true;
2965 aValue = rOption.GetString();
2966 break;
2967 case HtmlOptionId::NOWRAP:
2968 m_bNoWrap = true;
2969 break;
2970 default: break;
2971 }
2972 }
2973
2974 if( !m_aId.isEmpty() )
2975 rParser.InsertBookmark( m_aId );
2976 }
2977
2978 if( m_bHasNumFormat )
2979 {
2980 LanguageType eLang;
2981 m_nValue = SfxHTMLParser::GetTableDataOptionsValNum(
2982 m_nNumFormat, eLang, aValue, aNumFormat,
2983 *rParser.m_xDoc->GetNumberFormatter() );
2984 }
2985
2986 // Create a new context but don't anchor the drawing::Alignment attribute there,
2987 // since there's no section yet
2988 HtmlTokenId nToken;
2989 sal_uInt16 nColl;
2990 if( m_bHead )
2991 {
2992 nToken = HtmlTokenId::TABLEHEADER_ON;
2993 nColl = RES_POOLCOLL_TABLE_HDLN;
2994 }
2995 else
2996 {
2997 nToken = HtmlTokenId::TABLEDATA_ON;
2998 nColl = RES_POOLCOLL_TABLE;
2999 }
3000 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, OUString(), true));
3001 if( SvxAdjust::End != eAdjust )
3002 rParser.InsertAttr(&rParser.m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST),
3003 xCntxt.get());
3004
3005 if( SwHTMLParser::HasStyleOptions( m_aStyle, m_aId, m_aClass, &aLang, &aDir ) )
3006 {
3007 SfxItemSet aItemSet( rParser.m_xDoc->GetAttrPool(),
3008 rParser.m_pCSS1Parser->GetWhichMap() );
3009 SvxCSS1PropertyInfo aPropInfo;
3010
3011 if( rParser.ParseStyleOptions( m_aStyle, m_aId, m_aClass, aItemSet,
3012 aPropInfo, &aLang, &aDir ) )
3013 {
3014 SfxPoolItem const* pItem;
3015 if (SfxItemState::SET == aItemSet.GetItemState(RES_BOX, false, &pItem))
3016 { // fdo#41796: steal box item to set it in FixFrameFormat later!
3017 m_xBoxItem.reset(dynamic_cast<SvxBoxItem *>(pItem->Clone()));
3018 aItemSet.ClearItem(RES_BOX);
3019 }
3020 rParser.InsertAttrs(aItemSet, aPropInfo, xCntxt.get());
3021 }
3022 }
3023
3024 rParser.SplitPREListingXMP(xCntxt.get());
3025
3026 rParser.PushContext(xCntxt);
3027 }
3028
AddContents(std::unique_ptr<HTMLTableCnts> pNewCnts)3029 void CellSaveStruct::AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts )
3030 {
3031 m_pCurrCnts = pNewCnts.get();
3032
3033 if (m_xCnts)
3034 m_xCnts->Add( std::move(pNewCnts) );
3035 else
3036 m_xCnts = std::move(pNewCnts);
3037 }
3038
InsertCell(SwHTMLParser & rParser,HTMLTable * pCurTable)3039 void CellSaveStruct::InsertCell( SwHTMLParser& rParser,
3040 HTMLTable *pCurTable )
3041 {
3042 #if OSL_DEBUG_LEVEL > 0
3043 // The attributes need to have been removed when tidying up the context stack,
3044 // Otherwise something's wrong. Let's check that...
3045
3046 // MIB 8.1.98: When attributes were opened outside of a cell,
3047 // they're still in the attribute table and will only be deleted at the end
3048 // through the CleanContext calls in BuildTable. We don't check that there
3049 // so that we get no assert [violations, by translator]
3050 // We can see this on nContextStAttrMin: the remembered value of nContextStAttrMinSave
3051 // is the value that nContextStAttrMin had at the start of the table. And the
3052 // current value of nContextStAttrMin corresponds to the number of contexts
3053 // we found at the start of the cell. If the values differ, contexts
3054 // were created and we don't check anything.
3055
3056 if( rParser.m_nContextStAttrMin == GetContextStAttrMin() )
3057 {
3058 HTMLAttr** pTable = reinterpret_cast<HTMLAttr**>(rParser.m_xAttrTab.get());
3059
3060 for( auto nCnt = sizeof( HTMLAttrTable ) / sizeof( HTMLAttr* );
3061 nCnt--; ++pTable )
3062 {
3063 OSL_ENSURE( !*pTable, "The attribute table isn't empty" );
3064 }
3065 }
3066 #endif
3067
3068 // we need to add the cell on the current position
3069 std::shared_ptr<SvxBrushItem> xBrushItem(
3070 rParser.CreateBrushItem(m_bBGColor ? &m_aBGColor : nullptr, m_aBGImage,
3071 m_aStyle, m_aId, m_aClass));
3072 pCurTable->InsertCell( m_xCnts, m_nRowSpan, m_nColSpan, m_nWidth,
3073 m_bPrcWidth, m_nHeight, m_eVertOri, xBrushItem, m_xBoxItem,
3074 m_bHasNumFormat, m_nNumFormat, m_bHasValue, m_nValue,
3075 m_bNoWrap );
3076 Restore( rParser );
3077 }
3078
StartNoBreak(const SwPosition & rPos)3079 void CellSaveStruct::StartNoBreak( const SwPosition& rPos )
3080 {
3081 if( !m_xCnts ||
3082 (!rPos.nContent.GetIndex() && m_pCurrCnts == m_xCnts.get() &&
3083 m_xCnts->GetStartNode() &&
3084 m_xCnts->GetStartNode()->GetIndex() + 1 ==
3085 rPos.nNode.GetIndex()) )
3086 {
3087 m_bNoBreak = true;
3088 }
3089 }
3090
EndNoBreak(const SwPosition & rPos)3091 void CellSaveStruct::EndNoBreak( const SwPosition& rPos )
3092 {
3093 if( m_bNoBreak )
3094 {
3095 m_pNoBreakEndNodeIndex.reset( new SwNodeIndex( rPos.nNode ) );
3096 m_nNoBreakEndContentPos = rPos.nContent.GetIndex();
3097 m_bNoBreak = false;
3098 }
3099 }
3100
CheckNoBreak(const SwPosition & rPos)3101 void CellSaveStruct::CheckNoBreak( const SwPosition& rPos )
3102 {
3103 if (m_xCnts && m_pCurrCnts == m_xCnts.get())
3104 {
3105 if( m_bNoBreak )
3106 {
3107 // <NOBR> wasn't closed
3108 m_xCnts->SetNoBreak();
3109 }
3110 else if( m_pNoBreakEndNodeIndex &&
3111 m_pNoBreakEndNodeIndex->GetIndex() == rPos.nNode.GetIndex() )
3112 {
3113 if( m_nNoBreakEndContentPos == rPos.nContent.GetIndex() )
3114 {
3115 // <NOBR> was closed immediately before the cell end
3116 m_xCnts->SetNoBreak();
3117 }
3118 else if( m_nNoBreakEndContentPos + 1 == rPos.nContent.GetIndex() )
3119 {
3120 SwTextNode const*const pTextNd(rPos.nNode.GetNode().GetTextNode());
3121 if( pTextNd )
3122 {
3123 sal_Unicode const cLast =
3124 pTextNd->GetText()[m_nNoBreakEndContentPos];
3125 if( ' '==cLast || '\x0a'==cLast )
3126 {
3127 // There's just a blank or a newline between the <NOBR> and the cell end
3128 m_xCnts->SetNoBreak();
3129 }
3130 }
3131 }
3132 }
3133 }
3134 }
3135
InsertTableContents(bool bHead)3136 std::unique_ptr<HTMLTableCnts> SwHTMLParser::InsertTableContents(
3137 bool bHead )
3138 {
3139 // create a new section, the PaM is gonna be there
3140 const SwStartNode *pStNd =
3141 InsertTableSection( static_cast< sal_uInt16 >(bHead ? RES_POOLCOLL_TABLE_HDLN
3142 : RES_POOLCOLL_TABLE) );
3143
3144 if( GetNumInfo().GetNumRule() )
3145 {
3146 // Set the first paragraph to non-enumerated
3147 sal_uInt8 nLvl = GetNumInfo().GetLevel();
3148
3149 SetNodeNum( nLvl );
3150 }
3151
3152 // Reset attributation start
3153 const SwNodeIndex& rSttPara = m_pPam->GetPoint()->nNode;
3154 sal_Int32 nSttCnt = m_pPam->GetPoint()->nContent.GetIndex();
3155
3156 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
3157 for (sal_uInt16 nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
3158 {
3159 HTMLAttr *pAttr = *pHTMLAttributes;
3160 while( pAttr )
3161 {
3162 OSL_ENSURE( !pAttr->GetPrev(), "Attribute has previous list" );
3163 pAttr->m_nStartPara = rSttPara;
3164 pAttr->m_nEndPara = rSttPara;
3165 pAttr->m_nStartContent = nSttCnt;
3166 pAttr->m_nEndContent = nSttCnt;
3167
3168 pAttr = pAttr->GetNext();
3169 }
3170 }
3171
3172 return std::make_unique<HTMLTableCnts>( pStNd );
3173 }
3174
IncGrfsThatResizeTable()3175 sal_uInt16 SwHTMLParser::IncGrfsThatResizeTable()
3176 {
3177 return m_xTable ? m_xTable->IncGrfsThatResize() : 0;
3178 }
3179
RegisterDrawObjectToTable(HTMLTable * pCurTable,SdrObject * pObj,sal_uInt8 nPrcWidth)3180 void SwHTMLParser::RegisterDrawObjectToTable( HTMLTable *pCurTable,
3181 SdrObject *pObj, sal_uInt8 nPrcWidth )
3182 {
3183 pCurTable->RegisterDrawObject( pObj, nPrcWidth );
3184 }
3185
BuildTableCell(HTMLTable * pCurTable,bool bReadOptions,bool bHead)3186 void SwHTMLParser::BuildTableCell( HTMLTable *pCurTable, bool bReadOptions,
3187 bool bHead )
3188 {
3189 if( !IsParserWorking() && m_vPendingStack.empty() )
3190 return;
3191
3192 ::comphelper::FlagRestorationGuard g(m_isInTableStructure, false);
3193 std::unique_ptr<CellSaveStruct> xSaveStruct;
3194
3195 HtmlTokenId nToken = HtmlTokenId::NONE;
3196 bool bPending = false;
3197 if( !m_vPendingStack.empty() )
3198 {
3199 xSaveStruct.reset(static_cast<CellSaveStruct*>(m_vPendingStack.back().pData.release()));
3200
3201 m_vPendingStack.pop_back();
3202 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
3203 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
3204
3205 SaveState( nToken );
3206 }
3207 else
3208 {
3209 // <TH> resp. <TD> were already read
3210 if (m_xTable->IsOverflowing())
3211 {
3212 SaveState( HtmlTokenId::NONE );
3213 return;
3214 }
3215
3216 if( !pCurTable->GetContext() )
3217 {
3218 bool bTopTable = m_xTable.get() == pCurTable;
3219
3220 // the table has no content yet, this means the actual table needs
3221 // to be created first
3222
3223 static sal_uInt16 aWhichIds[] =
3224 {
3225 RES_PARATR_SPLIT, RES_PARATR_SPLIT,
3226 RES_PAGEDESC, RES_PAGEDESC,
3227 RES_BREAK, RES_BREAK,
3228 RES_BACKGROUND, RES_BACKGROUND,
3229 RES_KEEP, RES_KEEP,
3230 RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT,
3231 RES_FRAMEDIR, RES_FRAMEDIR,
3232 0
3233 };
3234
3235 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), aWhichIds );
3236 SvxCSS1PropertyInfo aPropInfo;
3237
3238 bool bStyleParsed = ParseStyleOptions( pCurTable->GetStyle(),
3239 pCurTable->GetId(),
3240 pCurTable->GetClass(),
3241 aItemSet, aPropInfo,
3242 nullptr, &pCurTable->GetDirection() );
3243 const SfxPoolItem *pItem = nullptr;
3244 if( bStyleParsed )
3245 {
3246 if( SfxItemState::SET == aItemSet.GetItemState(
3247 RES_BACKGROUND, false, &pItem ) )
3248 {
3249 pCurTable->SetBGBrush( *static_cast<const SvxBrushItem *>(pItem) );
3250 aItemSet.ClearItem( RES_BACKGROUND );
3251 }
3252 if( SfxItemState::SET == aItemSet.GetItemState(
3253 RES_PARATR_SPLIT, false, &pItem ) )
3254 {
3255 aItemSet.Put(
3256 SwFormatLayoutSplit( static_cast<const SvxFormatSplitItem *>(pItem)
3257 ->GetValue() ) );
3258 aItemSet.ClearItem( RES_PARATR_SPLIT );
3259 }
3260 }
3261
3262 sal_uInt16 nLeftSpace = 0;
3263 sal_uInt16 nRightSpace = 0;
3264 short nIndent;
3265 GetMarginsFromContextWithNumBul( nLeftSpace, nRightSpace, nIndent );
3266
3267 // save the current position we'll get back to some time
3268 SwPosition *pSavePos = nullptr;
3269 bool bForceFrame = false;
3270 bool bAppended = false;
3271 bool bParentLFStripped = false;
3272 if( bTopTable )
3273 {
3274 SvxAdjust eTableAdjust = m_xTable->GetTableAdjust(false);
3275
3276 // If the table is left or right adjusted or should be in a text frame,
3277 // it'll get one
3278 bForceFrame = eTableAdjust == SvxAdjust::Left ||
3279 eTableAdjust == SvxAdjust::Right ||
3280 pCurTable->HasToFly();
3281
3282 // The table either shouldn't get in a text frame and isn't in one
3283 // (it gets simulated through cells),
3284 // or there's already content at that position
3285 OSL_ENSURE( !bForceFrame || pCurTable->HasParentSection(),
3286 "table in frame has no parent!" );
3287
3288 bool bAppend = false;
3289 if( bForceFrame )
3290 {
3291 // If the table gets in a border, we only need to open a new
3292 //paragraph if the paragraph has text frames that don't fly
3293 bAppend = HasCurrentParaFlys(true);
3294 }
3295 else
3296 {
3297 // Otherwise, we need to open a new paragraph if the paragraph
3298 // is empty or contains text frames or bookmarks
3299 bAppend =
3300 m_pPam->GetPoint()->nContent.GetIndex() ||
3301 HasCurrentParaFlys() ||
3302 HasCurrentParaBookmarks();
3303 }
3304 if( bAppend )
3305 {
3306 if( !m_pPam->GetPoint()->nContent.GetIndex() )
3307 {
3308 //Set default to CJK and CTL
3309 m_xDoc->SetTextFormatColl( *m_pPam,
3310 m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_STANDARD) );
3311 SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
3312
3313 HTMLAttr* pTmp =
3314 new HTMLAttr( *m_pPam->GetPoint(), aFontHeight, nullptr, std::shared_ptr<HTMLAttrTable>() );
3315 m_aSetAttrTab.push_back( pTmp );
3316
3317 SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
3318 pTmp =
3319 new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCJK, nullptr, std::shared_ptr<HTMLAttrTable>() );
3320 m_aSetAttrTab.push_back( pTmp );
3321
3322 SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
3323 pTmp =
3324 new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCTL, nullptr, std::shared_ptr<HTMLAttrTable>() );
3325 m_aSetAttrTab.push_back( pTmp );
3326
3327 pTmp = new HTMLAttr( *m_pPam->GetPoint(),
3328 SvxULSpaceItem( 0, 0, RES_UL_SPACE ), nullptr, std::shared_ptr<HTMLAttrTable>() );
3329 m_aSetAttrTab.push_front( pTmp ); // Position 0, since
3330 // something can be set by
3331 // the table end before
3332 }
3333 AppendTextNode( AM_NOSPACE );
3334 bAppended = true;
3335 }
3336 else if( !m_aParaAttrs.empty() )
3337 {
3338 if( !bForceFrame )
3339 {
3340 // The paragraph will be moved right behind the table.
3341 // That's why we remove all hard attributes of that paragraph
3342
3343 for(HTMLAttr* i : m_aParaAttrs)
3344 i->Invalidate();
3345 }
3346
3347 m_aParaAttrs.clear();
3348 }
3349
3350 pSavePos = new SwPosition( *m_pPam->GetPoint() );
3351 }
3352 else if( pCurTable->HasParentSection() )
3353 {
3354 bParentLFStripped = StripTrailingLF() > 0;
3355
3356 // Close paragraph resp. headers
3357 m_nOpenParaToken = HtmlTokenId::NONE;
3358 m_nFontStHeadStart = m_nFontStMin;
3359
3360 // The hard attributes on that paragraph are never gonna be invalid anymore
3361 m_aParaAttrs.clear();
3362 }
3363
3364 // create a table context
3365 std::unique_ptr<HTMLTableContext> pTCntxt(
3366 new HTMLTableContext( pSavePos, m_nContextStMin,
3367 m_nContextStAttrMin ) );
3368
3369 // end all open attributes and open them again behind the table
3370 std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts;
3371 if( !bForceFrame && (bTopTable || pCurTable->HasParentSection()) )
3372 {
3373 SplitAttrTab(pTCntxt->xAttrTab, bTopTable);
3374 // If we reuse an already existing paragraph, we can't add
3375 // PostIts since the paragraph gets behind that table.
3376 // They're gonna be moved into the first paragraph of the table
3377 // If we have tables in tables, we also can't add PostIts to a
3378 // still empty paragraph, since it's not gonna be deleted that way
3379 if( (bTopTable && !bAppended) ||
3380 (!bTopTable && !bParentLFStripped &&
3381 !m_pPam->GetPoint()->nContent.GetIndex()) )
3382 pPostIts.reset(new std::deque<std::unique_ptr<HTMLAttr>>);
3383 SetAttr( bTopTable, bTopTable, pPostIts.get() );
3384 }
3385 else
3386 {
3387 SaveAttrTab(pTCntxt->xAttrTab);
3388 if( bTopTable && !bAppended )
3389 {
3390 pPostIts.reset(new std::deque<std::unique_ptr<HTMLAttr>>);
3391 SetAttr( true, true, pPostIts.get() );
3392 }
3393 }
3394 m_bNoParSpace = false;
3395
3396 // Save current numbering and turn it off
3397 pTCntxt->SetNumInfo( GetNumInfo() );
3398 GetNumInfo().Clear();
3399 pTCntxt->SavePREListingXMP( *this );
3400
3401 if( bTopTable )
3402 {
3403 if( bForceFrame )
3404 {
3405 // the table should be put in a text frame
3406
3407 SfxItemSet aFrameSet( m_xDoc->GetAttrPool(),
3408 svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} );
3409 if( !pCurTable->IsNewDoc() )
3410 Reader::ResetFrameFormatAttrs( aFrameSet );
3411
3412 css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
3413 sal_Int16 eHori;
3414
3415 switch( pCurTable->GetTableAdjust(true) )
3416 {
3417 case SvxAdjust::Right:
3418 eHori = text::HoriOrientation::RIGHT;
3419 eSurround = css::text::WrapTextMode_LEFT;
3420 break;
3421 case SvxAdjust::Center:
3422 eHori = text::HoriOrientation::CENTER;
3423 break;
3424 case SvxAdjust::Left:
3425 eSurround = css::text::WrapTextMode_RIGHT;
3426 [[fallthrough]];
3427 default:
3428 eHori = text::HoriOrientation::LEFT;
3429 break;
3430 }
3431 SetAnchorAndAdjustment( text::VertOrientation::NONE, eHori, aFrameSet,
3432 true );
3433 aFrameSet.Put( SwFormatSurround(eSurround) );
3434
3435 SwFormatFrameSize aFrameSize( ATT_VAR_SIZE, 20*MM50, MINLAY );
3436 aFrameSize.SetWidthPercent( 100 );
3437 aFrameSet.Put( aFrameSize );
3438
3439 sal_uInt16 nSpace = pCurTable->GetHSpace();
3440 if( nSpace )
3441 aFrameSet.Put( SvxLRSpaceItem(nSpace,nSpace, 0, 0, RES_LR_SPACE) );
3442 nSpace = pCurTable->GetVSpace();
3443 if( nSpace )
3444 aFrameSet.Put( SvxULSpaceItem(nSpace,nSpace, RES_UL_SPACE) );
3445
3446 RndStdIds eAnchorId = aFrameSet.
3447 Get( RES_ANCHOR ).
3448 GetAnchorId();
3449 SwFrameFormat *pFrameFormat = m_xDoc->MakeFlySection(
3450 eAnchorId, m_pPam->GetPoint(), &aFrameSet );
3451
3452 pTCntxt->SetFrameFormat( pFrameFormat );
3453 const SwFormatContent& rFlyContent = pFrameFormat->GetContent();
3454 m_pPam->GetPoint()->nNode = *rFlyContent.GetContentIdx();
3455 SwContentNode *pCNd =
3456 m_xDoc->GetNodes().GoNext( &(m_pPam->GetPoint()->nNode) );
3457 m_pPam->GetPoint()->nContent.Assign( pCNd, 0 );
3458
3459 }
3460
3461 // create a SwTable with a box and set the PaM to the content of
3462 // the box section (the adjustment parameter is a dummy for now
3463 // and will be corrected later)
3464 OSL_ENSURE( !m_pPam->GetPoint()->nContent.GetIndex(),
3465 "The paragraph after the table is not empty!" );
3466 const SwTable* pSwTable = m_xDoc->InsertTable(
3467 SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ),
3468 *m_pPam->GetPoint(), 1, 1, text::HoriOrientation::LEFT );
3469 SwFrameFormat *pFrameFormat = pSwTable ? pSwTable->GetFrameFormat() : nullptr;
3470
3471 if( bForceFrame )
3472 {
3473 SwNodeIndex aDstIdx( m_pPam->GetPoint()->nNode );
3474 m_pPam->Move( fnMoveBackward );
3475 m_xDoc->GetNodes().Delete( aDstIdx );
3476 }
3477 else
3478 {
3479 if (bStyleParsed && pFrameFormat)
3480 {
3481 m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo );
3482 pFrameFormat->SetFormatAttr( aItemSet );
3483 }
3484 m_pPam->Move( fnMoveBackward );
3485 }
3486
3487 SwNode const*const pNd = & m_pPam->GetPoint()->nNode.GetNode();
3488 SwTextNode *const pOldTextNd = (!bAppended && !bForceFrame) ?
3489 pSavePos->nNode.GetNode().GetTextNode() : nullptr;
3490
3491 if (pFrameFormat && pOldTextNd)
3492 {
3493 const SfxPoolItem* pItem2;
3494 if( SfxItemState::SET == pOldTextNd->GetSwAttrSet()
3495 .GetItemState( RES_PAGEDESC, false, &pItem2 ) &&
3496 static_cast<const SwFormatPageDesc *>(pItem2)->GetPageDesc() )
3497 {
3498 pFrameFormat->SetFormatAttr( *pItem2 );
3499 pOldTextNd->ResetAttr( RES_PAGEDESC );
3500 }
3501 if( SfxItemState::SET == pOldTextNd->GetSwAttrSet()
3502 .GetItemState( RES_BREAK, true, &pItem2 ) )
3503 {
3504 switch( static_cast<const SvxFormatBreakItem *>(pItem2)->GetBreak() )
3505 {
3506 case SvxBreak::PageBefore:
3507 case SvxBreak::PageAfter:
3508 case SvxBreak::PageBoth:
3509 pFrameFormat->SetFormatAttr( *pItem2 );
3510 pOldTextNd->ResetAttr( RES_BREAK );
3511 break;
3512 default:
3513 break;
3514 }
3515 }
3516 }
3517
3518 if( !bAppended && pPostIts )
3519 {
3520 // set still-existing PostIts to the first paragraph of the table
3521 InsertAttrs( std::move(*pPostIts) );
3522 pPostIts.reset();
3523 }
3524
3525 pTCntxt->SetTableNode( const_cast<SwTableNode *>(pNd->FindTableNode()) );
3526
3527 auto pTableNode = pTCntxt->GetTableNode();
3528 pCurTable->SetTable( pTableNode, std::move(pTCntxt),
3529 nLeftSpace, nRightSpace,
3530 pSwTable, bForceFrame );
3531
3532 OSL_ENSURE( !pPostIts, "unused PostIts" );
3533 }
3534 else
3535 {
3536 // still open sections need to be deleted
3537 if( EndSections( bParentLFStripped ) )
3538 bParentLFStripped = false;
3539
3540 if( pCurTable->HasParentSection() )
3541 {
3542 // after that, we remove a possibly redundant empty paragraph,
3543 // but only if it was empty before we stripped the LFs
3544 if( !bParentLFStripped )
3545 StripTrailingPara();
3546
3547 if( pPostIts )
3548 {
3549 // move still existing PostIts to the end of the current paragraph
3550 InsertAttrs( std::move(*pPostIts) );
3551 pPostIts.reset();
3552 }
3553 }
3554
3555 SwNode const*const pNd = & m_pPam->GetPoint()->nNode.GetNode();
3556 const SwStartNode *pStNd = (m_xTable->m_bFirstCell ? pNd->FindTableNode()
3557 : pNd->FindTableBoxStartNode() );
3558
3559 pCurTable->SetTable( pStNd, std::move(pTCntxt), nLeftSpace, nRightSpace );
3560 }
3561
3562 // Freeze the context stack, since there could be attributes set
3563 // outside of cells. Can't happen earlier, since there may be
3564 // searches in the stack
3565 m_nContextStMin = m_aContexts.size();
3566 m_nContextStAttrMin = m_nContextStMin;
3567 }
3568
3569 xSaveStruct.reset(new CellSaveStruct(*this, pCurTable, bHead, bReadOptions));
3570
3571 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
3572 SaveState( HtmlTokenId::NONE );
3573 }
3574
3575 if( nToken == HtmlTokenId::NONE )
3576 nToken = GetNextToken(); // Token after <TABLE>
3577
3578 bool bDone = false;
3579 while( (IsParserWorking() && !bDone) || bPending )
3580 {
3581 SaveState( nToken );
3582
3583 nToken = FilterToken( nToken );
3584
3585 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || xSaveStruct->IsInSection(),
3586 "Where is the section??" );
3587 if( m_vPendingStack.empty() && m_bCallNextToken && xSaveStruct->IsInSection() )
3588 {
3589 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
3590 NextToken( nToken );
3591 }
3592 else switch( nToken )
3593 {
3594 case HtmlTokenId::TABLEHEADER_ON:
3595 case HtmlTokenId::TABLEDATA_ON:
3596 case HtmlTokenId::TABLEROW_ON:
3597 case HtmlTokenId::TABLEROW_OFF:
3598 case HtmlTokenId::THEAD_ON:
3599 case HtmlTokenId::THEAD_OFF:
3600 case HtmlTokenId::TFOOT_ON:
3601 case HtmlTokenId::TFOOT_OFF:
3602 case HtmlTokenId::TBODY_ON:
3603 case HtmlTokenId::TBODY_OFF:
3604 case HtmlTokenId::TABLE_OFF:
3605 SkipToken();
3606 [[fallthrough]];
3607 case HtmlTokenId::TABLEHEADER_OFF:
3608 case HtmlTokenId::TABLEDATA_OFF:
3609 bDone = true;
3610 break;
3611 case HtmlTokenId::TABLE_ON:
3612 {
3613 bool bHasToFly = false;
3614 SvxAdjust eTabAdjust = SvxAdjust::End;
3615 if( m_vPendingStack.empty() )
3616 {
3617 // only if we create a new table, but not if we're still
3618 // reading in the table after a Pending
3619 xSaveStruct->m_xTable = m_xTable;
3620
3621 // HACK: create a section for a table that goes in a text frame
3622 if( !xSaveStruct->IsInSection() )
3623 {
3624 // The loop needs to be forward, since the
3625 // first option always wins
3626 bool bNeedsSection = false;
3627 const HTMLOptions& rHTMLOptions = GetOptions();
3628 for (const auto & rOption : rHTMLOptions)
3629 {
3630 if( HtmlOptionId::ALIGN==rOption.GetToken() )
3631 {
3632 SvxAdjust eAdjust = rOption.GetEnum( aHTMLPAlignTable, SvxAdjust::End );
3633 bNeedsSection = SvxAdjust::Left == eAdjust ||
3634 SvxAdjust::Right == eAdjust;
3635 break;
3636 }
3637 }
3638 if( bNeedsSection )
3639 {
3640 xSaveStruct->AddContents(
3641 InsertTableContents(bHead ) );
3642 }
3643 }
3644 else
3645 {
3646 // If Flys are anchored in the current paragraph,
3647 // the table needs to get in a text frame
3648 bHasToFly = HasCurrentParaFlys(false,true);
3649 }
3650
3651 // There could be a section in the cell
3652 eTabAdjust = m_xAttrTab->pAdjust
3653 ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()).
3654 GetAdjust()
3655 : SvxAdjust::End;
3656 }
3657
3658 std::shared_ptr<HTMLTable> xSubTable = BuildTable(eTabAdjust,
3659 bHead,
3660 xSaveStruct->IsInSection(),
3661 bHasToFly);
3662 if( SvParserState::Pending != GetStatus() )
3663 {
3664 // Only if the table is really complete
3665 if (xSubTable)
3666 {
3667 OSL_ENSURE( xSubTable->GetTableAdjust(false)!= SvxAdjust::Left &&
3668 xSubTable->GetTableAdjust(false)!= SvxAdjust::Right,
3669 "left or right aligned tables belong in frames" );
3670
3671 auto& rParentContents = xSubTable->GetParentContents();
3672 if (rParentContents)
3673 {
3674 OSL_ENSURE( !xSaveStruct->IsInSection(),
3675 "Where is the section" );
3676
3677 // If there's no table coming, we have a section
3678 xSaveStruct->AddContents(std::move(rParentContents));
3679 }
3680
3681 const SwStartNode *pCapStNd =
3682 xSubTable->GetCaptionStartNode();
3683
3684 if (xSubTable->GetContext())
3685 {
3686 OSL_ENSURE( !xSubTable->GetContext()->GetFrameFormat(),
3687 "table in frame" );
3688
3689 if( pCapStNd && xSubTable->IsTopCaption() )
3690 {
3691 xSaveStruct->AddContents(
3692 std::make_unique<HTMLTableCnts>(pCapStNd) );
3693 }
3694
3695 xSaveStruct->AddContents(
3696 std::make_unique<HTMLTableCnts>(xSubTable) );
3697
3698 if( pCapStNd && !xSubTable->IsTopCaption() )
3699 {
3700 xSaveStruct->AddContents(
3701 std::make_unique<HTMLTableCnts>(pCapStNd) );
3702 }
3703
3704 // We don't have a section anymore
3705 xSaveStruct->ClearIsInSection();
3706 }
3707 else if( pCapStNd )
3708 {
3709 // Since we can't delete this section (it might
3710 // belong to the first box), we'll add it
3711 xSaveStruct->AddContents(
3712 std::make_unique<HTMLTableCnts>(pCapStNd) );
3713
3714 // We don't have a section anymore
3715 xSaveStruct->ClearIsInSection();
3716 }
3717 }
3718
3719 m_xTable = xSaveStruct->m_xTable;
3720 }
3721 }
3722 break;
3723
3724 case HtmlTokenId::NOBR_ON:
3725 // HACK for MS: Is the <NOBR> at the start of the cell?
3726 xSaveStruct->StartNoBreak( *m_pPam->GetPoint() );
3727 break;
3728
3729 case HtmlTokenId::NOBR_OFF:
3730 xSaveStruct->EndNoBreak( *m_pPam->GetPoint() );
3731 break;
3732
3733 case HtmlTokenId::COMMENT:
3734 // Spaces are not gonna be deleted with comment fields,
3735 // and we don't want a new cell for a comment
3736 NextToken( nToken );
3737 break;
3738
3739 case HtmlTokenId::MARQUEE_ON:
3740 if( !xSaveStruct->IsInSection() )
3741 {
3742 // create a new section, the PaM is gonna be there
3743 xSaveStruct->AddContents(
3744 InsertTableContents( bHead ) );
3745 }
3746 m_bCallNextToken = true;
3747 NewMarquee( pCurTable );
3748 break;
3749
3750 case HtmlTokenId::TEXTTOKEN:
3751 // Don't add a section for an empty string
3752 if( !xSaveStruct->IsInSection() && 1==aToken.getLength() &&
3753 ' '==aToken[0] )
3754 break;
3755 [[fallthrough]];
3756 default:
3757 if( !xSaveStruct->IsInSection() )
3758 {
3759 // add a new section, the PaM's gonna be there
3760 xSaveStruct->AddContents(
3761 InsertTableContents( bHead ) );
3762 }
3763
3764 if( IsParserWorking() || bPending )
3765 NextToken( nToken );
3766 break;
3767 }
3768
3769 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
3770 "SwHTMLParser::BuildTableCell: There is a PendStack again" );
3771 bPending = false;
3772 if( IsParserWorking() )
3773 SaveState( HtmlTokenId::NONE );
3774
3775 if( !bDone )
3776 nToken = GetNextToken();
3777 }
3778
3779 if( SvParserState::Pending == GetStatus() )
3780 {
3781 m_vPendingStack.emplace_back( bHead ? HtmlTokenId::TABLEHEADER_ON
3782 : HtmlTokenId::TABLEDATA_ON );
3783 m_vPendingStack.back().pData = std::move(xSaveStruct);
3784
3785 return;
3786 }
3787
3788 // If the content of the cell was empty, we need to create an empty content
3789 // We also create an empty content if the cell ended with a table and had no
3790 // COL tags. Otherwise, it was probably exported by us and we don't
3791 // want to have an additional paragraph
3792 if( !xSaveStruct->HasFirstContents() ||
3793 (!xSaveStruct->IsInSection() && !pCurTable->HasColTags()) )
3794 {
3795 OSL_ENSURE( xSaveStruct->HasFirstContents() ||
3796 !xSaveStruct->IsInSection(),
3797 "Section or not, that is the question here" );
3798 const SwStartNode *pStNd =
3799 InsertTableSection( static_cast< sal_uInt16 >(xSaveStruct->IsHeaderCell()
3800 ? RES_POOLCOLL_TABLE_HDLN
3801 : RES_POOLCOLL_TABLE ));
3802
3803 if (!pStNd)
3804 eState = SvParserState::Error;
3805 else
3806 {
3807 const SwEndNode *pEndNd = pStNd->EndOfSectionNode();
3808 SwContentNode *pCNd = m_xDoc->GetNodes()[pEndNd->GetIndex()-1] ->GetContentNode();
3809 if (!pCNd)
3810 eState = SvParserState::Error;
3811 else
3812 {
3813 //Added defaults to CJK and CTL
3814 SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
3815 pCNd->SetAttr( aFontHeight );
3816 SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
3817 pCNd->SetAttr( aFontHeightCJK );
3818 SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
3819 pCNd->SetAttr( aFontHeightCTL );
3820 }
3821 }
3822
3823 xSaveStruct->AddContents( std::make_unique<HTMLTableCnts>(pStNd) );
3824 xSaveStruct->ClearIsInSection();
3825 }
3826
3827 if( xSaveStruct->IsInSection() )
3828 {
3829 xSaveStruct->CheckNoBreak( *m_pPam->GetPoint() );
3830
3831 // End all open contexts. We'll take AttrMin because nContextStMin might
3832 // have been modified. Since it's gonna be restored by EndContext, it's okay
3833 while( m_aContexts.size() > m_nContextStAttrMin+1 )
3834 {
3835 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3836 EndContext(xCntxt.get());
3837 }
3838
3839 // Remove LFs at the paragraph end
3840 if (StripTrailingLF() == 0 && !m_pPam->GetPoint()->nContent.GetIndex())
3841 {
3842 HTMLTableContext* pTableContext = m_xTable ? m_xTable->GetContext() : nullptr;
3843 SwPosition* pSavedPos = pTableContext ? pTableContext->GetPos() : nullptr;
3844 const bool bDeleteSafe = !pSavedPos || pSavedPos->nNode != m_pPam->GetPoint()->nNode;
3845 if (bDeleteSafe)
3846 StripTrailingPara();
3847 }
3848
3849 // If there was an adjustment set for the cell, we need to close it
3850 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3851 if (xCntxt)
3852 EndContext(xCntxt.get());
3853 }
3854 else
3855 {
3856 // Close all still open contexts
3857 while( m_aContexts.size() > m_nContextStAttrMin )
3858 {
3859 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3860 if (!xCntxt)
3861 break;
3862 ClearContext(xCntxt.get());
3863 }
3864 }
3865
3866 // end an enumeration
3867 GetNumInfo().Clear();
3868
3869 SetAttr( false );
3870
3871 xSaveStruct->InsertCell( *this, pCurTable );
3872
3873 // we're probably before a <TH>, <TD>, <TR> or </TABLE>
3874 xSaveStruct.reset();
3875 }
3876
3877 class RowSaveStruct : public SwPendingData
3878 {
3879 public:
3880 SvxAdjust eAdjust;
3881 sal_Int16 eVertOri;
3882 bool bHasCells;
3883
RowSaveStruct()3884 RowSaveStruct() :
3885 eAdjust( SvxAdjust::End ), eVertOri( text::VertOrientation::TOP ), bHasCells( false )
3886 {}
3887 };
3888
BuildTableRow(HTMLTable * pCurTable,bool bReadOptions,SvxAdjust eGrpAdjust,sal_Int16 eGrpVertOri)3889 void SwHTMLParser::BuildTableRow( HTMLTable *pCurTable, bool bReadOptions,
3890 SvxAdjust eGrpAdjust,
3891 sal_Int16 eGrpVertOri )
3892 {
3893 // <TR> was already read
3894
3895 if( !IsParserWorking() && m_vPendingStack.empty() )
3896 return;
3897
3898 HtmlTokenId nToken = HtmlTokenId::NONE;
3899 std::unique_ptr<RowSaveStruct> xSaveStruct;
3900
3901 bool bPending = false;
3902 if( !m_vPendingStack.empty() )
3903 {
3904 xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
3905
3906 m_vPendingStack.pop_back();
3907 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
3908 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
3909
3910 SaveState( nToken );
3911 }
3912 else
3913 {
3914 SvxAdjust eAdjust = eGrpAdjust;
3915 sal_Int16 eVertOri = eGrpVertOri;
3916 Color aBGColor;
3917 OUString aBGImage, aStyle, aId, aClass;
3918 bool bBGColor = false;
3919 xSaveStruct.reset(new RowSaveStruct);
3920
3921 if( bReadOptions )
3922 {
3923 const HTMLOptions& rHTMLOptions = GetOptions();
3924 for (size_t i = rHTMLOptions.size(); i; )
3925 {
3926 const HTMLOption& rOption = rHTMLOptions[--i];
3927 switch( rOption.GetToken() )
3928 {
3929 case HtmlOptionId::ID:
3930 aId = rOption.GetString();
3931 break;
3932 case HtmlOptionId::ALIGN:
3933 eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
3934 break;
3935 case HtmlOptionId::VALIGN:
3936 eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri );
3937 break;
3938 case HtmlOptionId::BGCOLOR:
3939 // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/>TH> like Netscape
3940 // *really* not on other tags
3941 if( !rOption.GetString().isEmpty() )
3942 {
3943 rOption.GetColor( aBGColor );
3944 bBGColor = true;
3945 }
3946 break;
3947 case HtmlOptionId::BACKGROUND:
3948 aBGImage = rOption.GetString();
3949 break;
3950 case HtmlOptionId::STYLE:
3951 aStyle = rOption.GetString();
3952 break;
3953 case HtmlOptionId::CLASS:
3954 aClass= rOption.GetString();
3955 break;
3956 default: break;
3957 }
3958 }
3959 }
3960
3961 if( !aId.isEmpty() )
3962 InsertBookmark( aId );
3963
3964 std::unique_ptr<SvxBrushItem> xBrushItem(
3965 CreateBrushItem( bBGColor ? &aBGColor : nullptr, aBGImage, aStyle,
3966 aId, aClass ));
3967 pCurTable->OpenRow(eAdjust, eVertOri, xBrushItem);
3968 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
3969 SaveState( HtmlTokenId::NONE );
3970 }
3971
3972 if( nToken == HtmlTokenId::NONE )
3973 nToken = GetNextToken();
3974
3975 bool bDone = false;
3976 while( (IsParserWorking() && !bDone) || bPending )
3977 {
3978 SaveState( nToken );
3979
3980 nToken = FilterToken( nToken );
3981
3982 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
3983 pCurTable->GetContext() || pCurTable->HasParentSection(),
3984 "Where is the section??" );
3985 if( m_vPendingStack.empty() && m_bCallNextToken &&
3986 (pCurTable->GetContext() || pCurTable->HasParentSection()) )
3987 {
3988 /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
3989 NextToken( nToken );
3990 }
3991 else switch( nToken )
3992 {
3993 case HtmlTokenId::TABLE_ON:
3994 if( !pCurTable->GetContext() )
3995 {
3996 SkipToken();
3997 bDone = true;
3998 }
3999
4000 break;
4001 case HtmlTokenId::TABLEROW_ON:
4002 case HtmlTokenId::THEAD_ON:
4003 case HtmlTokenId::THEAD_OFF:
4004 case HtmlTokenId::TBODY_ON:
4005 case HtmlTokenId::TBODY_OFF:
4006 case HtmlTokenId::TFOOT_ON:
4007 case HtmlTokenId::TFOOT_OFF:
4008 case HtmlTokenId::TABLE_OFF:
4009 SkipToken();
4010 [[fallthrough]];
4011 case HtmlTokenId::TABLEROW_OFF:
4012 bDone = true;
4013 break;
4014 case HtmlTokenId::TABLEHEADER_ON:
4015 case HtmlTokenId::TABLEDATA_ON:
4016 BuildTableCell( pCurTable, true, HtmlTokenId::TABLEHEADER_ON==nToken );
4017 if( SvParserState::Pending != GetStatus() )
4018 {
4019 xSaveStruct->bHasCells = true;
4020 bDone = m_xTable->IsOverflowing();
4021 }
4022 break;
4023 case HtmlTokenId::CAPTION_ON:
4024 BuildTableCaption( pCurTable );
4025 bDone = m_xTable->IsOverflowing();
4026 break;
4027 case HtmlTokenId::CAPTION_OFF:
4028 case HtmlTokenId::TABLEHEADER_OFF:
4029 case HtmlTokenId::TABLEDATA_OFF:
4030 case HtmlTokenId::COLGROUP_ON:
4031 case HtmlTokenId::COLGROUP_OFF:
4032 case HtmlTokenId::COL_ON:
4033 case HtmlTokenId::COL_OFF:
4034 // Where no cell started, there can't be a cell ending
4035 // all the other tokens are bogus anyway and only break the table
4036 break;
4037 case HtmlTokenId::MULTICOL_ON:
4038 // we can't add columned text frames here
4039 break;
4040 case HtmlTokenId::FORM_ON:
4041 NewForm( false ); // don't create a new paragraph
4042 break;
4043 case HtmlTokenId::FORM_OFF:
4044 EndForm( false ); // don't create a new paragraph
4045 break;
4046 case HtmlTokenId::COMMENT:
4047 NextToken( nToken );
4048 break;
4049 case HtmlTokenId::MAP_ON:
4050 // an image map doesn't add anything, so we can parse it without a cell
4051 NextToken( nToken );
4052 break;
4053 case HtmlTokenId::TEXTTOKEN:
4054 if( (pCurTable->GetContext() ||
4055 !pCurTable->HasParentSection()) &&
4056 1==aToken.getLength() && ' '==aToken[0] )
4057 break;
4058 [[fallthrough]];
4059 default:
4060 pCurTable->MakeParentContents();
4061 NextToken( nToken );
4062 break;
4063 }
4064
4065 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
4066 "SwHTMLParser::BuildTableRow: There is a PendStack again" );
4067 bPending = false;
4068 if( IsParserWorking() )
4069 SaveState( HtmlTokenId::NONE );
4070
4071 if( !bDone )
4072 nToken = GetNextToken();
4073 }
4074
4075 if( SvParserState::Pending == GetStatus() )
4076 {
4077 m_vPendingStack.emplace_back( HtmlTokenId::TABLEROW_ON );
4078 m_vPendingStack.back().pData = std::move(xSaveStruct);
4079 }
4080 else
4081 {
4082 pCurTable->CloseRow(!xSaveStruct->bHasCells);
4083 xSaveStruct.reset();
4084 }
4085
4086 // we're probably before <TR> or </TABLE>
4087 }
4088
BuildTableSection(HTMLTable * pCurTable,bool bReadOptions,bool bHead)4089 void SwHTMLParser::BuildTableSection( HTMLTable *pCurTable,
4090 bool bReadOptions,
4091 bool bHead )
4092 {
4093 // <THEAD>, <TBODY> resp. <TFOOT> were read already
4094 if( !IsParserWorking() && m_vPendingStack.empty() )
4095 return;
4096
4097 HtmlTokenId nToken = HtmlTokenId::NONE;
4098 bool bPending = false;
4099 std::unique_ptr<RowSaveStruct> xSaveStruct;
4100
4101 if( !m_vPendingStack.empty() )
4102 {
4103 xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
4104
4105 m_vPendingStack.pop_back();
4106 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4107 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4108
4109 SaveState( nToken );
4110 }
4111 else
4112 {
4113 xSaveStruct.reset(new RowSaveStruct);
4114
4115 if( bReadOptions )
4116 {
4117 const HTMLOptions& rHTMLOptions = GetOptions();
4118 for (size_t i = rHTMLOptions.size(); i; )
4119 {
4120 const HTMLOption& rOption = rHTMLOptions[--i];
4121 switch( rOption.GetToken() )
4122 {
4123 case HtmlOptionId::ID:
4124 InsertBookmark( rOption.GetString() );
4125 break;
4126 case HtmlOptionId::ALIGN:
4127 xSaveStruct->eAdjust =
4128 rOption.GetEnum( aHTMLPAlignTable, xSaveStruct->eAdjust );
4129 break;
4130 case HtmlOptionId::VALIGN:
4131 xSaveStruct->eVertOri =
4132 rOption.GetEnum( aHTMLTableVAlignTable,
4133 xSaveStruct->eVertOri );
4134 break;
4135 default: break;
4136 }
4137 }
4138 }
4139
4140 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4141 SaveState( HtmlTokenId::NONE );
4142 }
4143
4144 if( nToken == HtmlTokenId::NONE )
4145 nToken = GetNextToken();
4146
4147 bool bDone = false;
4148 while( (IsParserWorking() && !bDone) || bPending )
4149 {
4150 SaveState( nToken );
4151
4152 nToken = FilterToken( nToken );
4153
4154 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
4155 pCurTable->GetContext() || pCurTable->HasParentSection(),
4156 "Where is the section?" );
4157 if( m_vPendingStack.empty() && m_bCallNextToken &&
4158 (pCurTable->GetContext() || pCurTable->HasParentSection()) )
4159 {
4160 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
4161 NextToken( nToken );
4162 }
4163 else switch( nToken )
4164 {
4165 case HtmlTokenId::TABLE_ON:
4166 if( !pCurTable->GetContext() )
4167 {
4168 SkipToken();
4169 bDone = true;
4170 }
4171
4172 break;
4173 case HtmlTokenId::THEAD_ON:
4174 case HtmlTokenId::TFOOT_ON:
4175 case HtmlTokenId::TBODY_ON:
4176 case HtmlTokenId::TABLE_OFF:
4177 SkipToken();
4178 [[fallthrough]];
4179 case HtmlTokenId::THEAD_OFF:
4180 case HtmlTokenId::TBODY_OFF:
4181 case HtmlTokenId::TFOOT_OFF:
4182 bDone = true;
4183 break;
4184 case HtmlTokenId::CAPTION_ON:
4185 BuildTableCaption( pCurTable );
4186 bDone = m_xTable->IsOverflowing();
4187 break;
4188 case HtmlTokenId::CAPTION_OFF:
4189 break;
4190 case HtmlTokenId::TABLEHEADER_ON:
4191 case HtmlTokenId::TABLEDATA_ON:
4192 SkipToken();
4193 BuildTableRow( pCurTable, false, xSaveStruct->eAdjust,
4194 xSaveStruct->eVertOri );
4195 bDone = m_xTable->IsOverflowing();
4196 break;
4197 case HtmlTokenId::TABLEROW_ON:
4198 BuildTableRow( pCurTable, true, xSaveStruct->eAdjust,
4199 xSaveStruct->eVertOri );
4200 bDone = m_xTable->IsOverflowing();
4201 break;
4202 case HtmlTokenId::MULTICOL_ON:
4203 // we can't add columned text frames here
4204 break;
4205 case HtmlTokenId::FORM_ON:
4206 NewForm( false ); // don't create a new paragraph
4207 break;
4208 case HtmlTokenId::FORM_OFF:
4209 EndForm( false ); // don't create a new paragraph
4210 break;
4211 case HtmlTokenId::TEXTTOKEN:
4212 // blank strings may be a series of CR+LF and no text
4213 if( (pCurTable->GetContext() ||
4214 !pCurTable->HasParentSection()) &&
4215 1==aToken.getLength() && ' ' == aToken[0] )
4216 break;
4217 [[fallthrough]];
4218 default:
4219 pCurTable->MakeParentContents();
4220 NextToken( nToken );
4221 }
4222
4223 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
4224 "SwHTMLParser::BuildTableSection: There is a PendStack again" );
4225 bPending = false;
4226 if( IsParserWorking() )
4227 SaveState( HtmlTokenId::NONE );
4228
4229 if( !bDone )
4230 nToken = GetNextToken();
4231 }
4232
4233 if( SvParserState::Pending == GetStatus() )
4234 {
4235 m_vPendingStack.emplace_back( bHead ? HtmlTokenId::THEAD_ON
4236 : HtmlTokenId::TBODY_ON );
4237 m_vPendingStack.back().pData = std::move(xSaveStruct);
4238 }
4239 else
4240 {
4241 pCurTable->CloseSection( bHead );
4242 xSaveStruct.reset();
4243 }
4244
4245 // now we stand (perhaps) in front of <TBODY>,... or </TABLE>
4246 }
4247
4248 struct TableColGrpSaveStruct : public SwPendingData
4249 {
4250 sal_uInt16 nColGrpSpan;
4251 sal_uInt16 nColGrpWidth;
4252 bool bRelColGrpWidth;
4253 SvxAdjust eColGrpAdjust;
4254 sal_Int16 eColGrpVertOri;
4255
4256 inline TableColGrpSaveStruct();
4257
4258 inline void CloseColGroup( HTMLTable *pTable );
4259 };
4260
TableColGrpSaveStruct()4261 inline TableColGrpSaveStruct::TableColGrpSaveStruct() :
4262 nColGrpSpan( 1 ), nColGrpWidth( 0 ),
4263 bRelColGrpWidth( false ), eColGrpAdjust( SvxAdjust::End ),
4264 eColGrpVertOri( text::VertOrientation::TOP )
4265 {}
4266
CloseColGroup(HTMLTable * pTable)4267 inline void TableColGrpSaveStruct::CloseColGroup( HTMLTable *pTable )
4268 {
4269 pTable->CloseColGroup( nColGrpSpan, nColGrpWidth,
4270 bRelColGrpWidth, eColGrpAdjust, eColGrpVertOri );
4271 }
4272
BuildTableColGroup(HTMLTable * pCurTable,bool bReadOptions)4273 void SwHTMLParser::BuildTableColGroup( HTMLTable *pCurTable,
4274 bool bReadOptions )
4275 {
4276 // <COLGROUP> was read already if bReadOptions is set
4277
4278 if( !IsParserWorking() && m_vPendingStack.empty() )
4279 return;
4280
4281 HtmlTokenId nToken = HtmlTokenId::NONE;
4282 bool bPending = false;
4283 std::unique_ptr<TableColGrpSaveStruct> pSaveStruct;
4284
4285 if( !m_vPendingStack.empty() )
4286 {
4287 pSaveStruct.reset(static_cast<TableColGrpSaveStruct*>(m_vPendingStack.back().pData.release()));
4288
4289
4290 m_vPendingStack.pop_back();
4291 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4292 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4293
4294 SaveState( nToken );
4295 }
4296 else
4297 {
4298
4299 pSaveStruct.reset(new TableColGrpSaveStruct);
4300 if( bReadOptions )
4301 {
4302 const HTMLOptions& rColGrpOptions = GetOptions();
4303 for (size_t i = rColGrpOptions.size(); i; )
4304 {
4305 const HTMLOption& rOption = rColGrpOptions[--i];
4306 switch( rOption.GetToken() )
4307 {
4308 case HtmlOptionId::ID:
4309 InsertBookmark( rOption.GetString() );
4310 break;
4311 case HtmlOptionId::SPAN:
4312 pSaveStruct->nColGrpSpan = static_cast<sal_uInt16>(rOption.GetNumber());
4313 if (pSaveStruct->nColGrpSpan > 256)
4314 {
4315 SAL_INFO("sw.html", "ignoring huge SPAN " << pSaveStruct->nColGrpSpan);
4316 pSaveStruct->nColGrpSpan = 1;
4317 }
4318 break;
4319 case HtmlOptionId::WIDTH:
4320 pSaveStruct->nColGrpWidth = static_cast<sal_uInt16>(rOption.GetNumber());
4321 pSaveStruct->bRelColGrpWidth =
4322 (rOption.GetString().indexOf('*') != -1);
4323 break;
4324 case HtmlOptionId::ALIGN:
4325 pSaveStruct->eColGrpAdjust =
4326 rOption.GetEnum( aHTMLPAlignTable, pSaveStruct->eColGrpAdjust );
4327 break;
4328 case HtmlOptionId::VALIGN:
4329 pSaveStruct->eColGrpVertOri =
4330 rOption.GetEnum( aHTMLTableVAlignTable,
4331 pSaveStruct->eColGrpVertOri );
4332 break;
4333 default: break;
4334 }
4335 }
4336 }
4337 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4338 SaveState( HtmlTokenId::NONE );
4339 }
4340
4341 if( nToken == HtmlTokenId::NONE )
4342 nToken = GetNextToken(); // naechstes Token
4343
4344 bool bDone = false;
4345 while( (IsParserWorking() && !bDone) || bPending )
4346 {
4347 SaveState( nToken );
4348
4349 nToken = FilterToken( nToken );
4350
4351 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
4352 pCurTable->GetContext() || pCurTable->HasParentSection(),
4353 "Where is the section?" );
4354 if( m_vPendingStack.empty() && m_bCallNextToken &&
4355 (pCurTable->GetContext() || pCurTable->HasParentSection()) )
4356 {
4357 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
4358 NextToken( nToken );
4359 }
4360 else switch( nToken )
4361 {
4362 case HtmlTokenId::TABLE_ON:
4363 if( !pCurTable->GetContext() )
4364 {
4365 SkipToken();
4366 bDone = true;
4367 }
4368
4369 break;
4370 case HtmlTokenId::COLGROUP_ON:
4371 case HtmlTokenId::THEAD_ON:
4372 case HtmlTokenId::TFOOT_ON:
4373 case HtmlTokenId::TBODY_ON:
4374 case HtmlTokenId::TABLEROW_ON:
4375 case HtmlTokenId::TABLE_OFF:
4376 SkipToken();
4377 [[fallthrough]];
4378 case HtmlTokenId::COLGROUP_OFF:
4379 bDone = true;
4380 break;
4381 case HtmlTokenId::COL_ON:
4382 {
4383 sal_uInt16 nColSpan = 1;
4384 sal_uInt16 nColWidth = pSaveStruct->nColGrpWidth;
4385 bool bRelColWidth = pSaveStruct->bRelColGrpWidth;
4386 SvxAdjust eColAdjust = pSaveStruct->eColGrpAdjust;
4387 sal_Int16 eColVertOri = pSaveStruct->eColGrpVertOri;
4388
4389 const HTMLOptions& rColOptions = GetOptions();
4390 for (size_t i = rColOptions.size(); i; )
4391 {
4392 const HTMLOption& rOption = rColOptions[--i];
4393 switch( rOption.GetToken() )
4394 {
4395 case HtmlOptionId::ID:
4396 InsertBookmark( rOption.GetString() );
4397 break;
4398 case HtmlOptionId::SPAN:
4399 nColSpan = static_cast<sal_uInt16>(rOption.GetNumber());
4400 if (nColSpan > 256)
4401 {
4402 SAL_INFO("sw.html", "ignoring huge SPAN " << nColSpan);
4403 nColSpan = 1;
4404 }
4405 break;
4406 case HtmlOptionId::WIDTH:
4407 nColWidth = static_cast<sal_uInt16>(rOption.GetNumber());
4408 bRelColWidth =
4409 (rOption.GetString().indexOf('*') != -1);
4410 break;
4411 case HtmlOptionId::ALIGN:
4412 eColAdjust = rOption.GetEnum( aHTMLPAlignTable, eColAdjust );
4413 break;
4414 case HtmlOptionId::VALIGN:
4415 eColVertOri =
4416 rOption.GetEnum( aHTMLTableVAlignTable, eColVertOri );
4417 break;
4418 default: break;
4419 }
4420 }
4421 pCurTable->InsertCol( nColSpan, nColWidth, bRelColWidth,
4422 eColAdjust, eColVertOri );
4423
4424 // the attributes in <COLGRP> should be ignored, if there are <COL> elements
4425 pSaveStruct->nColGrpSpan = 0;
4426 }
4427 break;
4428 case HtmlTokenId::COL_OFF:
4429 break; // Ignore
4430 case HtmlTokenId::MULTICOL_ON:
4431 // we can't add columned text frames here
4432 break;
4433 case HtmlTokenId::TEXTTOKEN:
4434 if( (pCurTable->GetContext() ||
4435 !pCurTable->HasParentSection()) &&
4436 1==aToken.getLength() && ' '==aToken[0] )
4437 break;
4438 [[fallthrough]];
4439 default:
4440 pCurTable->MakeParentContents();
4441 NextToken( nToken );
4442 }
4443
4444 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
4445 "SwHTMLParser::BuildTableColGrp: There is a PendStack again" );
4446 bPending = false;
4447 if( IsParserWorking() )
4448 SaveState( HtmlTokenId::NONE );
4449
4450 if( !bDone )
4451 nToken = GetNextToken();
4452 }
4453
4454 if( SvParserState::Pending == GetStatus() )
4455 {
4456 m_vPendingStack.emplace_back( HtmlTokenId::COL_ON );
4457 m_vPendingStack.back().pData = std::move(pSaveStruct);
4458 }
4459 else
4460 {
4461 pSaveStruct->CloseColGroup( pCurTable );
4462 }
4463 }
4464
4465 class CaptionSaveStruct : public SectionSaveStruct
4466 {
4467 SwPosition const aSavePos;
4468 SwHTMLNumRuleInfo aNumRuleInfo; // valid numbering
4469
4470 public:
4471
4472 std::shared_ptr<HTMLAttrTable> xAttrTab; // attributes
4473
CaptionSaveStruct(SwHTMLParser & rParser,const SwPosition & rPos)4474 CaptionSaveStruct( SwHTMLParser& rParser, const SwPosition& rPos ) :
4475 SectionSaveStruct( rParser ), aSavePos( rPos ),
4476 xAttrTab(new HTMLAttrTable)
4477 {
4478 rParser.SaveAttrTab(xAttrTab);
4479
4480 // The current numbering was remembered and just needs to be closed
4481 aNumRuleInfo.Set( rParser.GetNumInfo() );
4482 rParser.GetNumInfo().Clear();
4483 }
4484
GetPos() const4485 const SwPosition& GetPos() const { return aSavePos; }
4486
RestoreAll(SwHTMLParser & rParser)4487 void RestoreAll( SwHTMLParser& rParser )
4488 {
4489 // Recover the old stack
4490 Restore( rParser );
4491
4492 // Recover the old attribute tables
4493 rParser.RestoreAttrTab(xAttrTab);
4494
4495 // Re-open the old numbering
4496 rParser.GetNumInfo().Set( aNumRuleInfo );
4497 }
4498 };
4499
BuildTableCaption(HTMLTable * pCurTable)4500 void SwHTMLParser::BuildTableCaption( HTMLTable *pCurTable )
4501 {
4502 // <CAPTION> was read already
4503
4504 if( !IsParserWorking() && m_vPendingStack.empty() )
4505 return;
4506
4507 HtmlTokenId nToken = HtmlTokenId::NONE;
4508 std::unique_ptr<CaptionSaveStruct> xSaveStruct;
4509
4510 if( !m_vPendingStack.empty() )
4511 {
4512 xSaveStruct.reset(static_cast<CaptionSaveStruct*>(m_vPendingStack.back().pData.release()));
4513
4514 m_vPendingStack.pop_back();
4515 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4516 OSL_ENSURE( m_vPendingStack.empty(), "Where does a PendStack coming from?" );
4517
4518 SaveState( nToken );
4519 }
4520 else
4521 {
4522 if (m_xTable->IsOverflowing())
4523 {
4524 SaveState( HtmlTokenId::NONE );
4525 return;
4526 }
4527
4528 bool bTop = true;
4529 const HTMLOptions& rHTMLOptions = GetOptions();
4530 for ( size_t i = rHTMLOptions.size(); i; )
4531 {
4532 const HTMLOption& rOption = rHTMLOptions[--i];
4533 if( HtmlOptionId::ALIGN == rOption.GetToken() )
4534 {
4535 if (rOption.GetString().equalsIgnoreAsciiCase(
4536 OOO_STRING_SVTOOLS_HTML_VA_bottom))
4537 {
4538 bTop = false;
4539 }
4540 }
4541 }
4542
4543 // Remember old PaM position
4544 xSaveStruct.reset(new CaptionSaveStruct(*this, *m_pPam->GetPoint()));
4545
4546 // Add a text section in the icon section as a container for the header
4547 // and set the PaM there
4548 const SwStartNode *pStNd;
4549 if (m_xTable.get() == pCurTable)
4550 pStNd = InsertTempTableCaptionSection();
4551 else
4552 pStNd = InsertTableSection( RES_POOLCOLL_TEXT );
4553
4554 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::CAPTION_ON));
4555
4556 // Table headers are always centered
4557 NewAttr(m_xAttrTab, &m_xAttrTab->pAdjust, SvxAdjustItem(SvxAdjust::Center, RES_PARATR_ADJUST));
4558
4559 HTMLAttrs &rAttrs = xCntxt->GetAttrs();
4560 rAttrs.push_back( m_xAttrTab->pAdjust );
4561
4562 PushContext(xCntxt);
4563
4564 // Remember the start node of the section at the table
4565 pCurTable->SetCaption( pStNd, bTop );
4566
4567 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4568 SaveState( HtmlTokenId::NONE );
4569 }
4570
4571 if( nToken == HtmlTokenId::NONE )
4572 nToken = GetNextToken();
4573
4574 // </CAPTION> is needed according to DTD
4575 bool bDone = false;
4576 while( IsParserWorking() && !bDone )
4577 {
4578 SaveState( nToken );
4579
4580 nToken = FilterToken( nToken );
4581
4582 switch( nToken )
4583 {
4584 case HtmlTokenId::TABLE_ON:
4585 if( m_vPendingStack.empty() )
4586 {
4587 xSaveStruct->m_xTable = m_xTable;
4588 bool bHasToFly = xSaveStruct->m_xTable.get() != pCurTable;
4589 BuildTable( pCurTable->GetTableAdjust( true ),
4590 false, true, bHasToFly );
4591 }
4592 else
4593 {
4594 BuildTable( SvxAdjust::End );
4595 }
4596 if( SvParserState::Pending != GetStatus() )
4597 {
4598 m_xTable = xSaveStruct->m_xTable;
4599 }
4600 break;
4601 case HtmlTokenId::TABLE_OFF:
4602 case HtmlTokenId::COLGROUP_ON:
4603 case HtmlTokenId::THEAD_ON:
4604 case HtmlTokenId::TFOOT_ON:
4605 case HtmlTokenId::TBODY_ON:
4606 case HtmlTokenId::TABLEROW_ON:
4607 SkipToken();
4608 bDone = true;
4609 break;
4610
4611 case HtmlTokenId::CAPTION_OFF:
4612 bDone = true;
4613 break;
4614 default:
4615 if( !m_vPendingStack.empty() )
4616 {
4617 m_vPendingStack.pop_back();
4618 OSL_ENSURE( m_vPendingStack.empty(), "Further it can't go!" );
4619 }
4620
4621 if( IsParserWorking() )
4622 NextToken( nToken );
4623 break;
4624 }
4625
4626 if( IsParserWorking() )
4627 SaveState( HtmlTokenId::NONE );
4628
4629 if( !bDone )
4630 nToken = GetNextToken();
4631 }
4632
4633 if( SvParserState::Pending==GetStatus() )
4634 {
4635 m_vPendingStack.emplace_back( HtmlTokenId::CAPTION_ON );
4636 m_vPendingStack.back().pData = std::move(xSaveStruct);
4637 return;
4638 }
4639
4640 // end all still open contexts
4641 while( m_aContexts.size() > m_nContextStAttrMin+1 )
4642 {
4643 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
4644 EndContext(xCntxt.get());
4645 }
4646
4647 bool bLFStripped = StripTrailingLF() > 0;
4648
4649 if (m_xTable.get() == pCurTable)
4650 {
4651 // On moving the caption later, the last paragraph isn't moved as well.
4652 // That means, there has to be an empty paragraph at the end of the section
4653 if( m_pPam->GetPoint()->nContent.GetIndex() || bLFStripped )
4654 AppendTextNode( AM_NOSPACE );
4655 }
4656 else
4657 {
4658 // Strip LFs at the end of the paragraph
4659 if( !m_pPam->GetPoint()->nContent.GetIndex() && !bLFStripped )
4660 StripTrailingPara();
4661 }
4662
4663 // If there's an adjustment for the cell, we need to close it
4664 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
4665 if (xCntxt)
4666 {
4667 EndContext(xCntxt.get());
4668 xCntxt.reset();
4669 }
4670
4671 SetAttr( false );
4672
4673 // Recover stack and attribute table
4674 xSaveStruct->RestoreAll(*this);
4675
4676 // Recover PaM
4677 *m_pPam->GetPoint() = xSaveStruct->GetPos();
4678 }
4679
4680 class TableSaveStruct : public SwPendingData
4681 {
4682 public:
4683 std::shared_ptr<HTMLTable> m_xCurrentTable;
4684
TableSaveStruct(const std::shared_ptr<HTMLTable> & rCurTable)4685 explicit TableSaveStruct(const std::shared_ptr<HTMLTable>& rCurTable)
4686 : m_xCurrentTable(rCurTable)
4687 {
4688 }
4689
4690 // Initiate creation of the table and put the table in a text frame if
4691 // needed. If it returns true, we need to insert a paragraph.
4692 void MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc );
4693 };
4694
MakeTable(sal_uInt16 nWidth,SwPosition & rPos,SwDoc * pDoc)4695 void TableSaveStruct::MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc )
4696 {
4697 m_xCurrentTable->MakeTable(nullptr, nWidth);
4698
4699 HTMLTableContext *pTCntxt = m_xCurrentTable->GetContext();
4700 OSL_ENSURE( pTCntxt, "Where is the table context" );
4701
4702 SwTableNode *pTableNd = pTCntxt->GetTableNode();
4703 OSL_ENSURE( pTableNd, "Where is the table node" );
4704
4705 if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pTableNd )
4706 {
4707 // If there's already a layout, the BoxFrames need to be regenerated at this table
4708
4709 if( pTCntxt->GetFrameFormat() )
4710 {
4711 pTCntxt->GetFrameFormat()->DelFrames();
4712 pTableNd->DelFrames();
4713 pTCntxt->GetFrameFormat()->MakeFrames();
4714 }
4715 else
4716 {
4717 pTableNd->DelFrames();
4718 SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
4719 OSL_ENSURE( aIdx.GetIndex() <= pTCntxt->GetPos()->nNode.GetIndex(),
4720 "unexpected node for table layout" );
4721 pTableNd->MakeOwnFrames(&aIdx);
4722 }
4723 }
4724
4725 rPos = *pTCntxt->GetPos();
4726 }
4727
HTMLTableOptions(const HTMLOptions & rOptions,SvxAdjust eParentAdjust)4728 HTMLTableOptions::HTMLTableOptions( const HTMLOptions& rOptions,
4729 SvxAdjust eParentAdjust ) :
4730 nCols( 0 ),
4731 nWidth( 0 ), nHeight( 0 ),
4732 nCellPadding( USHRT_MAX ), nCellSpacing( USHRT_MAX ),
4733 nBorder( USHRT_MAX ),
4734 nHSpace( 0 ), nVSpace( 0 ),
4735 eAdjust( eParentAdjust ), eVertOri( text::VertOrientation::CENTER ),
4736 eFrame( HTMLTableFrame::Void ), eRules( HTMLTableRules::NONE ),
4737 bPrcWidth( false ),
4738 bTableAdjust( false ),
4739 bBGColor( false ),
4740 aBorderColor( COL_GRAY )
4741 {
4742 bool bBorderColor = false;
4743 bool bHasFrame = false, bHasRules = false;
4744
4745 for (size_t i = rOptions.size(); i; )
4746 {
4747 const HTMLOption& rOption = rOptions[--i];
4748 switch( rOption.GetToken() )
4749 {
4750 case HtmlOptionId::ID:
4751 aId = rOption.GetString();
4752 break;
4753 case HtmlOptionId::COLS:
4754 nCols = static_cast<sal_uInt16>(rOption.GetNumber());
4755 break;
4756 case HtmlOptionId::WIDTH:
4757 nWidth = static_cast<sal_uInt16>(rOption.GetNumber());
4758 bPrcWidth = (rOption.GetString().indexOf('%') != -1);
4759 if( bPrcWidth && nWidth>100 )
4760 nWidth = 100;
4761 break;
4762 case HtmlOptionId::HEIGHT:
4763 nHeight = static_cast<sal_uInt16>(rOption.GetNumber());
4764 if( rOption.GetString().indexOf('%') != -1 )
4765 nHeight = 0; // don't use % attributes
4766 break;
4767 case HtmlOptionId::CELLPADDING:
4768 nCellPadding = static_cast<sal_uInt16>(rOption.GetNumber());
4769 break;
4770 case HtmlOptionId::CELLSPACING:
4771 nCellSpacing = static_cast<sal_uInt16>(rOption.GetNumber());
4772 break;
4773 case HtmlOptionId::ALIGN:
4774 {
4775 if( rOption.GetEnum( eAdjust, aHTMLPAlignTable ) )
4776 {
4777 bTableAdjust = true;
4778 }
4779 }
4780 break;
4781 case HtmlOptionId::VALIGN:
4782 eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri );
4783 break;
4784 case HtmlOptionId::BORDER:
4785 // Handle BORDER and BORDER=BORDER like BORDER=1
4786 if (!rOption.GetString().isEmpty() &&
4787 !rOption.GetString().equalsIgnoreAsciiCase(
4788 OOO_STRING_SVTOOLS_HTML_O_border))
4789 {
4790 nBorder = static_cast<sal_uInt16>(rOption.GetNumber());
4791 }
4792 else
4793 nBorder = 1;
4794
4795 if( !bHasFrame )
4796 eFrame = ( nBorder ? HTMLTableFrame::Box : HTMLTableFrame::Void );
4797 if( !bHasRules )
4798 eRules = ( nBorder ? HTMLTableRules::All : HTMLTableRules::NONE );
4799 break;
4800 case HtmlOptionId::FRAME:
4801 eFrame = rOption.GetTableFrame();
4802 bHasFrame = true;
4803 break;
4804 case HtmlOptionId::RULES:
4805 eRules = rOption.GetTableRules();
4806 bHasRules = true;
4807 break;
4808 case HtmlOptionId::BGCOLOR:
4809 // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape
4810 // *really* not on other tags
4811 if( !rOption.GetString().isEmpty() )
4812 {
4813 rOption.GetColor( aBGColor );
4814 bBGColor = true;
4815 }
4816 break;
4817 case HtmlOptionId::BACKGROUND:
4818 aBGImage = rOption.GetString();
4819 break;
4820 case HtmlOptionId::BORDERCOLOR:
4821 rOption.GetColor( aBorderColor );
4822 bBorderColor = true;
4823 break;
4824 case HtmlOptionId::BORDERCOLORDARK:
4825 if( !bBorderColor )
4826 rOption.GetColor( aBorderColor );
4827 break;
4828 case HtmlOptionId::STYLE:
4829 aStyle = rOption.GetString();
4830 break;
4831 case HtmlOptionId::CLASS:
4832 aClass = rOption.GetString();
4833 break;
4834 case HtmlOptionId::DIR:
4835 aDir = rOption.GetString();
4836 break;
4837 case HtmlOptionId::HSPACE:
4838 nHSpace = static_cast<sal_uInt16>(rOption.GetNumber());
4839 break;
4840 case HtmlOptionId::VSPACE:
4841 nVSpace = static_cast<sal_uInt16>(rOption.GetNumber());
4842 break;
4843 default: break;
4844 }
4845 }
4846
4847 if( nCols && !nWidth )
4848 {
4849 nWidth = 100;
4850 bPrcWidth = true;
4851 }
4852
4853 // If BORDER=0 or no BORDER given, then there shouldn't be a border
4854 if( 0==nBorder || USHRT_MAX==nBorder )
4855 {
4856 eFrame = HTMLTableFrame::Void;
4857 eRules = HTMLTableRules::NONE;
4858 }
4859 }
4860
4861 namespace
4862 {
4863 class FrameDeleteWatch final: public SvtListener
4864 {
4865 SwFrameFormat* m_pFormat;
4866 public:
FrameDeleteWatch(SwFrameFormat * pFormat)4867 FrameDeleteWatch(SwFrameFormat* pFormat)
4868 : m_pFormat(pFormat)
4869 {
4870 if(m_pFormat)
4871 StartListening(pFormat->GetNotifier());
4872 }
4873
Notify(const SfxHint & rHint)4874 virtual void Notify(const SfxHint& rHint) override
4875 {
4876 if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint))
4877 {
4878 if (pDrawFrameFormatHint->m_eId == sw::DrawFrameFormatHintId::DYING)
4879 {
4880 m_pFormat = nullptr;
4881 EndListeningAll();
4882 }
4883 }
4884 }
4885
WasDeleted() const4886 bool WasDeleted() const
4887 {
4888 return !m_pFormat;
4889 }
4890
~FrameDeleteWatch()4891 virtual ~FrameDeleteWatch() override
4892 {
4893 m_pFormat = nullptr;
4894 EndListeningAll();
4895 }
4896 };
4897
4898 class IndexInRange
4899 {
4900 private:
4901 SwNodeIndex const maStart;
4902 SwNodeIndex const maEnd;
4903 public:
IndexInRange(const SwNodeIndex & rStart,const SwNodeIndex & rEnd)4904 explicit IndexInRange(const SwNodeIndex& rStart, const SwNodeIndex& rEnd)
4905 : maStart(rStart)
4906 , maEnd(rEnd)
4907 {
4908 }
operator ()(const SwHTMLTextFootnote & rTextFootnote) const4909 bool operator()(const SwHTMLTextFootnote& rTextFootnote) const
4910 {
4911 const SwNodeIndex aTextIdx(rTextFootnote.pTextFootnote->GetTextNode());
4912 return aTextIdx >= maStart && aTextIdx <= maEnd;
4913 }
4914 };
4915 }
4916
ClearFootnotesMarksInRange(const SwNodeIndex & rMkNdIdx,const SwNodeIndex & rPtNdIdx)4917 void SwHTMLParser::ClearFootnotesMarksInRange(const SwNodeIndex& rMkNdIdx, const SwNodeIndex& rPtNdIdx)
4918 {
4919 //similarly for footnotes
4920 if (m_pFootEndNoteImpl)
4921 {
4922 m_pFootEndNoteImpl->aTextFootnotes.erase(std::remove_if(m_pFootEndNoteImpl->aTextFootnotes.begin(),
4923 m_pFootEndNoteImpl->aTextFootnotes.end(), IndexInRange(rMkNdIdx, rPtNdIdx)), m_pFootEndNoteImpl->aTextFootnotes.end());
4924 if (m_pFootEndNoteImpl->aTextFootnotes.empty())
4925 {
4926 m_pFootEndNoteImpl.reset();
4927 }
4928 }
4929
4930 //follow DelFlyInRange pattern here
4931 assert(rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex());
4932
4933 SwDoc* pDoc = rMkNdIdx.GetNode().GetDoc();
4934
4935 //ofz#9733 drop bookmarks in this range
4936 IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
4937 pMarkAccess->deleteMarks(rMkNdIdx, SwNodeIndex(rPtNdIdx, 1), nullptr, nullptr, nullptr);
4938
4939 SwFrameFormats& rTable = *pDoc->GetSpzFrameFormats();
4940 for ( auto i = rTable.size(); i; )
4941 {
4942 SwFrameFormat *pFormat = rTable[--i];
4943 const SwFormatAnchor &rAnch = pFormat->GetAnchor();
4944 SwPosition const*const pAPos = rAnch.GetContentAnchor();
4945 if (pAPos &&
4946 ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
4947 (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)) &&
4948 ( rMkNdIdx < pAPos->nNode && pAPos->nNode <= rPtNdIdx ))
4949 {
4950 if( rPtNdIdx != pAPos->nNode )
4951 {
4952 // If the Fly is deleted, all Flys in its content have to be deleted too.
4953 const SwFormatContent &rContent = pFormat->GetContent();
4954 // But only fly formats own their content, not draw formats.
4955 if (rContent.GetContentIdx() && pFormat->Which() == RES_FLYFRMFMT)
4956 {
4957 ClearFootnotesMarksInRange(*rContent.GetContentIdx(),
4958 SwNodeIndex(*rContent.GetContentIdx()->GetNode().EndOfSectionNode()));
4959 }
4960 }
4961 }
4962 }
4963 }
4964
DeleteSection(SwStartNode * pSttNd)4965 void SwHTMLParser::DeleteSection(SwStartNode* pSttNd)
4966 {
4967 //if section to be deleted contains a pending m_pMarquee, it will be deleted
4968 //so clear m_pMarquee pointer if that's the case
4969 SwFrameFormat* pObjectFormat = m_pMarquee ? ::FindFrameFormat(m_pMarquee) : nullptr;
4970 FrameDeleteWatch aWatch(pObjectFormat);
4971
4972 //similarly for footnotes
4973 SwNodeIndex aSttIdx(*pSttNd), aEndIdx(*pSttNd->EndOfSectionNode());
4974 ClearFootnotesMarksInRange(aSttIdx, aEndIdx);
4975
4976 m_xDoc->getIDocumentContentOperations().DeleteSection(pSttNd);
4977
4978 if (pObjectFormat)
4979 {
4980 if (aWatch.WasDeleted())
4981 m_pMarquee = nullptr;
4982 else
4983 aWatch.EndListeningAll();
4984 }
4985 }
4986
BuildTable(SvxAdjust eParentAdjust,bool bIsParentHead,bool bHasParentSection,bool bHasToFly)4987 std::shared_ptr<HTMLTable> SwHTMLParser::BuildTable(SvxAdjust eParentAdjust,
4988 bool bIsParentHead,
4989 bool bHasParentSection,
4990 bool bHasToFly)
4991 {
4992 TableDepthGuard aGuard(*this);
4993 if (aGuard.TooDeep())
4994 eState = SvParserState::Error;
4995
4996 if (!IsParserWorking() && m_vPendingStack.empty())
4997 return std::shared_ptr<HTMLTable>();
4998
4999 ::comphelper::FlagRestorationGuard g(m_isInTableStructure, true);
5000 HtmlTokenId nToken = HtmlTokenId::NONE;
5001 bool bPending = false;
5002 std::unique_ptr<TableSaveStruct> xSaveStruct;
5003
5004 if( !m_vPendingStack.empty() )
5005 {
5006 xSaveStruct.reset(static_cast<TableSaveStruct*>(m_vPendingStack.back().pData.release()));
5007
5008 m_vPendingStack.pop_back();
5009 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
5010 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
5011
5012 SaveState( nToken );
5013 }
5014 else
5015 {
5016 m_xTable.reset();
5017 HTMLTableOptions aTableOptions(GetOptions(), eParentAdjust);
5018
5019 if (!aTableOptions.aId.isEmpty())
5020 InsertBookmark(aTableOptions.aId);
5021
5022 std::shared_ptr<HTMLTable> xCurTable(std::make_shared<HTMLTable>(this,
5023 bIsParentHead,
5024 bHasParentSection,
5025 bHasToFly,
5026 aTableOptions));
5027 m_xTable = xCurTable;
5028
5029 xSaveStruct.reset(new TableSaveStruct(xCurTable));
5030
5031 // Is pending on the first GetNextToken, needs to be re-read on each construction
5032 SaveState( HtmlTokenId::NONE );
5033 }
5034
5035 std::shared_ptr<HTMLTable> xCurTable = xSaveStruct->m_xCurrentTable;
5036
5037 // </TABLE> is needed according to DTD
5038 if( nToken == HtmlTokenId::NONE )
5039 nToken = GetNextToken();
5040
5041 bool bDone = false;
5042 while( (IsParserWorking() && !bDone) || bPending )
5043 {
5044 SaveState( nToken );
5045
5046 nToken = FilterToken( nToken );
5047
5048 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
5049 xCurTable->GetContext() || xCurTable->HasParentSection(),
5050 "Where is the section?" );
5051 if( m_vPendingStack.empty() && m_bCallNextToken &&
5052 (xCurTable->GetContext() || xCurTable->HasParentSection()) )
5053 {
5054 /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
5055 NextToken( nToken );
5056 }
5057 else switch( nToken )
5058 {
5059 case HtmlTokenId::TABLE_ON:
5060 if( !xCurTable->GetContext() )
5061 {
5062 // If there's no table added, read the next table'
5063 SkipToken();
5064 bDone = true;
5065 }
5066
5067 break;
5068 case HtmlTokenId::TABLE_OFF:
5069 bDone = true;
5070 break;
5071 case HtmlTokenId::CAPTION_ON:
5072 BuildTableCaption(xCurTable.get());
5073 bDone = m_xTable->IsOverflowing();
5074 break;
5075 case HtmlTokenId::COL_ON:
5076 SkipToken();
5077 BuildTableColGroup(xCurTable.get(), false);
5078 break;
5079 case HtmlTokenId::COLGROUP_ON:
5080 BuildTableColGroup(xCurTable.get(), true);
5081 break;
5082 case HtmlTokenId::TABLEROW_ON:
5083 case HtmlTokenId::TABLEHEADER_ON:
5084 case HtmlTokenId::TABLEDATA_ON:
5085 SkipToken();
5086 BuildTableSection(xCurTable.get(), false, false);
5087 bDone = m_xTable->IsOverflowing();
5088 break;
5089 case HtmlTokenId::THEAD_ON:
5090 case HtmlTokenId::TFOOT_ON:
5091 case HtmlTokenId::TBODY_ON:
5092 BuildTableSection(xCurTable.get(), true, HtmlTokenId::THEAD_ON==nToken);
5093 bDone = m_xTable->IsOverflowing();
5094 break;
5095 case HtmlTokenId::MULTICOL_ON:
5096 // we can't add columned text frames here
5097 break;
5098 case HtmlTokenId::FORM_ON:
5099 NewForm( false ); // don't add a new paragraph
5100 break;
5101 case HtmlTokenId::FORM_OFF:
5102 EndForm( false ); // don't add a new paragraph
5103 break;
5104 case HtmlTokenId::TEXTTOKEN:
5105 // blank strings may be a series of CR+LF and no text
5106 if( (xCurTable->GetContext() ||
5107 !xCurTable->HasParentSection()) &&
5108 1==aToken.getLength() && ' '==aToken[0] )
5109 break;
5110 [[fallthrough]];
5111 default:
5112 xCurTable->MakeParentContents();
5113 NextToken( nToken );
5114 break;
5115 }
5116
5117 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
5118 "SwHTMLParser::BuildTable: There is a PendStack again" );
5119 bPending = false;
5120 if( IsParserWorking() )
5121 SaveState( HtmlTokenId::NONE );
5122
5123 if( !bDone )
5124 nToken = GetNextToken();
5125 }
5126
5127 if( SvParserState::Pending == GetStatus() )
5128 {
5129 m_vPendingStack.emplace_back( HtmlTokenId::TABLE_ON );
5130 m_vPendingStack.back().pData = std::move(xSaveStruct);
5131 return std::shared_ptr<HTMLTable>();
5132 }
5133
5134 HTMLTableContext *pTCntxt = xCurTable->GetContext();
5135 if( pTCntxt )
5136 {
5137
5138 // Modify table structure
5139 xCurTable->CloseTable();
5140
5141 // end contexts that began out of cells. Needs to exist before (!) we move the table,
5142 // since the current one doesn't exist anymore afterwards
5143 while( m_aContexts.size() > m_nContextStAttrMin )
5144 {
5145 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
5146 if (!xCntxt)
5147 break;
5148 ClearContext(xCntxt.get());
5149 }
5150
5151 m_nContextStMin = pTCntxt->GetContextStMin();
5152 m_nContextStAttrMin = pTCntxt->GetContextStAttrMin();
5153
5154 if (m_xTable == xCurTable)
5155 {
5156 // Set table caption
5157 const SwStartNode *pCapStNd = m_xTable->GetCaptionStartNode();
5158 if( pCapStNd )
5159 {
5160 // The last paragraph of the section is never part of the copy.
5161 // That's why the section needs to contain at least two paragraphs
5162
5163 if( pCapStNd->EndOfSectionIndex() - pCapStNd->GetIndex() > 2 )
5164 {
5165 // Don't copy start node and the last paragraph
5166 SwNodeRange aSrcRg( *pCapStNd, 1,
5167 *pCapStNd->EndOfSectionNode(), -1 );
5168
5169 bool bTop = m_xTable->IsTopCaption();
5170 SwStartNode *pTableStNd = pTCntxt->GetTableNode();
5171
5172 OSL_ENSURE( pTableStNd, "Where is the table node" );
5173 OSL_ENSURE( pTableStNd==m_pPam->GetNode().FindTableNode(),
5174 "Are we in the wrong table?" );
5175
5176 SwNode* pNd;
5177 if( bTop )
5178 pNd = pTableStNd;
5179 else
5180 pNd = pTableStNd->EndOfSectionNode();
5181 SwNodeIndex aDstIdx( *pNd, bTop ? 0 : 1 );
5182
5183 m_xDoc->getIDocumentContentOperations().MoveNodeRange( aSrcRg, aDstIdx,
5184 SwMoveFlags::DEFAULT );
5185
5186 // If the caption was added before the table, a page style on that table
5187 // needs to be moved to the first paragraph of the header.
5188 // Additionally, all remembered indices that point to the table node
5189 // need to be moved
5190 if( bTop )
5191 {
5192 MovePageDescAttrs( pTableStNd, aSrcRg.aStart.GetIndex(),
5193 false );
5194 }
5195 }
5196
5197 // The section isn't needed anymore
5198 m_pPam->SetMark();
5199 m_pPam->DeleteMark();
5200 DeleteSection(const_cast<SwStartNode*>(pCapStNd));
5201 m_xTable->SetCaption( nullptr, false );
5202 }
5203
5204 // Process SwTable
5205 sal_uInt16 nBrowseWidth = static_cast<sal_uInt16>(GetCurrentBrowseWidth());
5206 xSaveStruct->MakeTable(nBrowseWidth, *m_pPam->GetPoint(), m_xDoc.get());
5207 }
5208
5209 GetNumInfo().Set( pTCntxt->GetNumInfo() );
5210 pTCntxt->RestorePREListingXMP( *this );
5211 RestoreAttrTab(pTCntxt->xAttrTab);
5212
5213 if (m_xTable == xCurTable)
5214 {
5215 // Set upper paragraph spacing
5216 m_bUpperSpace = true;
5217 SetTextCollAttrs();
5218
5219 SwTableNode* pTableNode = pTCntxt->GetTableNode();
5220 size_t nTableBoxSize = pTableNode ? pTableNode->GetTable().GetTabSortBoxes().size() : 0;
5221 m_nParaCnt = m_nParaCnt - std::min(m_nParaCnt, nTableBoxSize);
5222
5223 // Jump to a table if needed
5224 if( JumpToMarks::Table == m_eJumpTo && m_xTable->GetSwTable() &&
5225 m_xTable->GetSwTable()->GetFrameFormat()->GetName() == m_sJmpMark )
5226 {
5227 m_bChkJumpMark = true;
5228 m_eJumpTo = JumpToMarks::NONE;
5229 }
5230
5231 // If the import was canceled, don't call Show again here since
5232 // the SwViewShell was already deleted
5233 // That's not enough. Even in the ACCEPTING_STATE, a Show mustn't be called
5234 // because otherwise the parser's gonna be destroyed on the reschedule,
5235 // if there's still a DataAvailable link coming. So: only in the WORKING state
5236 if( !m_nParaCnt && SvParserState::Working == GetStatus() )
5237 Show();
5238 }
5239 }
5240 else if (m_xTable == xCurTable)
5241 {
5242 // There was no table read
5243
5244 // We maybe need to delete a read caption
5245 const SwStartNode *pCapStNd = xCurTable->GetCaptionStartNode();
5246 if( pCapStNd )
5247 {
5248 m_pPam->SetMark();
5249 m_pPam->DeleteMark();
5250 DeleteSection(const_cast<SwStartNode*>(pCapStNd));
5251 xCurTable->SetCaption( nullptr, false );
5252 }
5253 }
5254
5255 if (m_xTable == xCurTable)
5256 {
5257 xSaveStruct->m_xCurrentTable.reset();
5258 m_xTable.reset();
5259 }
5260
5261 std::shared_ptr<HTMLTable> xRetTable = xSaveStruct->m_xCurrentTable;
5262 xSaveStruct.reset();
5263
5264 return xRetTable;
5265 }
5266
PendingDrawObjectsInPaM(SwPaM & rPam) const5267 bool HTMLTable::PendingDrawObjectsInPaM(SwPaM& rPam) const
5268 {
5269 if (!m_pResizeDrawObjects)
5270 return false;
5271
5272 bool bRet = false;
5273
5274 sal_uInt16 nCount = m_pResizeDrawObjects->size();
5275 for (sal_uInt16 i = 0; i < nCount && !bRet; ++i)
5276 {
5277 SdrObject *pObj = (*m_pResizeDrawObjects)[i];
5278 SwFrameFormat* pObjectFormat = ::FindFrameFormat(pObj);
5279 if (!pObjectFormat)
5280 continue;
5281 const SwFormatAnchor& rAnch = pObjectFormat->GetAnchor();
5282 if (const SwPosition* pPos = rAnch.GetContentAnchor())
5283 {
5284 SwNodeIndex aObjNodeIndex(pPos->nNode);
5285 bRet = (aObjNodeIndex >= rPam.Start()->nNode && aObjNodeIndex <= rPam.End()->nNode);
5286 }
5287 }
5288
5289 return bRet;
5290 }
5291
PendingObjectsInPaM(SwPaM & rPam) const5292 bool SwHTMLParser::PendingObjectsInPaM(SwPaM& rPam) const
5293 {
5294 bool bRet = false;
5295 for (const auto& a : m_aTables)
5296 {
5297 bRet = a->PendingDrawObjectsInPaM(rPam);
5298 if (bRet)
5299 break;
5300 const SwTable *pTable = a->GetSwTable();
5301 if (!pTable)
5302 continue;
5303 const SwTableNode* pTableNode = pTable->GetTableNode();
5304 if (!pTableNode)
5305 continue;
5306 SwNodeIndex aTableNodeIndex(*pTableNode);
5307 bRet = (aTableNodeIndex >= rPam.Start()->nNode && aTableNodeIndex <= rPam.End()->nNode);
5308 if (bRet)
5309 break;
5310 }
5311 return bRet;
5312 }
5313
5314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5315