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