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