sw: prefix members of CaptionSaveStruct, HTMLTableRow, ...
[LibreOffice.git] / sw / source / filter / html / htmltab.cxx
blob6c5ec09b2578af4499c777871f57fe21e634d39c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
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>
69 #define NETSCAPE_DFLT_BORDER 1
70 #define NETSCAPE_DFLT_CELLSPACING 2
72 using ::editeng::SvxBorderLine;
73 using namespace ::com::sun::star;
75 HTMLOptionEnum<sal_Int16> const aHTMLTableVAlignTable[] =
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 }
83 // table tags options
85 namespace {
87 struct HTMLTableOptions
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;
98 SvxAdjust eAdjust;
99 sal_Int16 eVertOri;
100 HTMLTableFrame eFrame;
101 HTMLTableRules eRules;
103 bool bPercentWidth : 1;
104 bool bTableAdjust : 1;
105 bool bBGColor : 1;
107 Color aBorderColor;
108 Color aBGColor;
110 OUString aBGImage, aStyle, aId, aClass, aDir;
112 HTMLTableOptions( const HTMLOptions& rOptions, SvxAdjust eParentAdjust );
115 class HTMLTableContext
117 SwHTMLNumRuleInfo m_aNumRuleInfo; // Numbering valid before the table
119 SwTableNode *m_pTableNd; // table node
120 SwFrameFormat *m_pFrameFormat; // the Fly frame::Frame, containing the table
121 std::unique_ptr<SwPosition> m_pPos; // position behind the table
123 size_t m_nContextStAttrMin;
124 size_t m_nContextStMin;
126 bool m_bRestartPRE : 1;
127 bool m_bRestartXMP : 1;
128 bool m_bRestartListing : 1;
130 HTMLTableContext(const HTMLTableContext&) = delete;
131 HTMLTableContext& operator=(const HTMLTableContext&) = delete;
133 public:
135 std::shared_ptr<HTMLAttrTable> m_xAttrTab; // attributes
137 HTMLTableContext( SwPosition *pPs, size_t nCntxtStMin,
138 size_t nCntxtStAttrMin ) :
139 m_pTableNd( nullptr ),
140 m_pFrameFormat( nullptr ),
141 m_pPos( pPs ),
142 m_nContextStAttrMin( nCntxtStAttrMin ),
143 m_nContextStMin( nCntxtStMin ),
144 m_bRestartPRE( false ),
145 m_bRestartXMP( false ),
146 m_bRestartListing( false ),
147 m_xAttrTab(std::make_shared<HTMLAttrTable>())
149 memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable));
152 void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { m_aNumRuleInfo.Set(rInf); }
153 const SwHTMLNumRuleInfo& GetNumInfo() const { return m_aNumRuleInfo; };
155 void SavePREListingXMP( SwHTMLParser& rParser );
156 void RestorePREListingXMP( SwHTMLParser& rParser );
158 SwPosition *GetPos() const { return m_pPos.get(); }
160 void SetTableNode( SwTableNode *pNd ) { m_pTableNd = pNd; }
161 SwTableNode *GetTableNode() const { return m_pTableNd; }
163 void SetFrameFormat( SwFrameFormat *pFormat ) { m_pFrameFormat = pFormat; }
164 SwFrameFormat *GetFrameFormat() const { return m_pFrameFormat; }
166 size_t GetContextStMin() const { return m_nContextStMin; }
167 size_t GetContextStAttrMin() const { return m_nContextStAttrMin; }
172 // Cell content is a linked list with SwStartNodes and
173 // HTMLTables.
175 class HTMLTableCnts
177 std::unique_ptr<HTMLTableCnts> m_pNext; // next content
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
183 std::shared_ptr<SwHTMLTableLayoutCnts> m_xLayoutInfo;
185 bool m_bNoBreak;
187 void InitCtor();
189 public:
191 explicit HTMLTableCnts(const SwStartNode* pStNd);
192 explicit HTMLTableCnts(const std::shared_ptr<HTMLTable>& rTab);
194 ~HTMLTableCnts(); // only allowed in ~HTMLTableCell
196 // Determine SwStartNode and HTMLTable respectively
197 const SwStartNode *GetStartNode() const { return m_pStartNode; }
198 const std::shared_ptr<HTMLTable>& GetTable() const { return m_xTable; }
199 std::shared_ptr<HTMLTable>& GetTable() { return m_xTable; }
201 // Add a new node at the end of the list
202 void Add( std::unique_ptr<HTMLTableCnts> pNewCnts );
204 // Determine next node
205 const HTMLTableCnts *Next() const { return m_pNext.get(); }
206 HTMLTableCnts *Next() { return m_pNext.get(); }
208 inline void SetTableBox( SwTableBox *pBox );
210 void SetNoBreak() { m_bNoBreak = true; }
212 const std::shared_ptr<SwHTMLTableLayoutCnts>& CreateLayoutInfo();
215 namespace {
217 // Cell of a HTML table
218 class HTMLTableCell
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;
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;
237 public:
239 HTMLTableCell(); // new cells always empty
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 );
248 // Protect an empty 1x1 cell
249 void SetProtected();
251 // Set/Get cell content
252 void SetContents(std::shared_ptr<HTMLTableCnts> const& rCnts) { m_xContents = rCnts; }
253 const std::shared_ptr<HTMLTableCnts>& GetContents() const { return m_xContents; }
255 // Set/Get cell ROWSPAN/COLSPAN
256 void SetRowSpan( sal_uInt16 nRSpan ) { m_nRowSpan = nRSpan; }
257 sal_uInt16 GetRowSpan() const { return m_nRowSpan; }
259 void SetColSpan( sal_uInt16 nCSpan ) { m_nColSpan = nCSpan; }
260 sal_uInt16 GetColSpan() const { return m_nColSpan; }
262 inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth );
264 const std::shared_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
265 const std::shared_ptr<SvxBoxItem>& GetBoxItem() const { return m_xBoxItem; }
267 inline bool GetNumFormat( sal_uInt32& rNumFormat ) const;
268 inline bool GetValue( double& rValue ) const;
270 sal_Int16 GetVertOri() const { return m_eVertOrient; }
272 // Is the cell filled or protected ?
273 bool IsUsed() const { return m_xContents || m_bProtected; }
275 std::unique_ptr<SwHTMLTableLayoutCell> CreateLayoutInfo();
277 bool IsCovered() const { return mbCovered; }
283 namespace {
285 // Row of a HTML table
286 class HTMLTableRow
288 std::vector<HTMLTableCell> m_aCells; ///< cells of the row
289 std::unique_ptr<SvxBrushItem> m_xBGBrush; // background of cell from STYLE
291 SvxAdjust m_eAdjust;
292 sal_uInt16 m_nHeight; // options of <TR>/<TD>
293 sal_uInt16 m_nEmptyRows; // number of empty rows are following
294 sal_Int16 m_eVertOri;
295 bool m_bIsEndOfGroup : 1;
296 bool m_bBottomBorder : 1; // Is there a line after the row?
298 public:
300 explicit HTMLTableRow( sal_uInt16 nCells ); // cells of the row are empty
302 void SetBottomBorder(bool bIn) { m_bBottomBorder = bIn; }
303 bool GetBottomBorder() const { return m_bBottomBorder; }
305 inline void SetHeight( sal_uInt16 nHeight );
306 sal_uInt16 GetHeight() const { return m_nHeight; }
308 const HTMLTableCell& GetCell(sal_uInt16 nCell) const;
309 HTMLTableCell& GetCell(sal_uInt16 nCell)
311 return const_cast<HTMLTableCell&>(const_cast<const HTMLTableRow&>(*this).GetCell(nCell));
314 void SetAdjust( SvxAdjust eAdj ) { m_eAdjust = eAdj; }
315 SvxAdjust GetAdjust() const { return m_eAdjust; }
317 void SetVertOri( sal_Int16 eV) { m_eVertOri = eV; }
318 sal_Int16 GetVertOri() const { return m_eVertOri; }
320 void SetBGBrush(std::unique_ptr<SvxBrushItem>& rBrush ) { m_xBGBrush = std::move(rBrush); }
321 const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
323 void SetEndOfGroup() { m_bIsEndOfGroup = true; }
324 bool IsEndOfGroup() const { return m_bIsEndOfGroup; }
326 void IncEmptyRows() { m_nEmptyRows++; }
327 sal_uInt16 GetEmptyRows() const { return m_nEmptyRows; }
329 // Expand row by adding empty cells
330 void Expand( sal_uInt16 nCells, bool bOneCell=false );
332 // Shrink row by deleting empty cells
333 void Shrink( sal_uInt16 nCells );
336 // Column of a HTML table
337 class HTMLTableColumn
339 bool m_bIsEndOfGroup;
341 sal_uInt16 m_nWidth; // options of <COL>
342 bool m_bRelWidth;
344 SvxAdjust m_eAdjust;
345 sal_Int16 m_eVertOri;
347 SwFrameFormat *m_aFrameFormats[6];
349 static inline sal_uInt16 GetFrameFormatIdx( bool bBorderLine,
350 sal_Int16 eVertOri );
352 public:
354 bool m_bLeftBorder; // is there a line before the column
356 HTMLTableColumn();
358 inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth);
360 void SetAdjust( SvxAdjust eAdj ) { m_eAdjust = eAdj; }
361 SvxAdjust GetAdjust() const { return m_eAdjust; }
363 void SetVertOri( sal_Int16 eV) { m_eVertOri = eV; }
364 sal_Int16 GetVertOri() const { return m_eVertOri; }
366 void SetEndOfGroup() { m_bIsEndOfGroup = true; }
367 bool IsEndOfGroup() const { return m_bIsEndOfGroup; }
369 inline void SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
370 sal_Int16 eVertOri );
371 inline SwFrameFormat *GetFrameFormat( bool bBorderLine,
372 sal_Int16 eVertOri ) const;
374 std::unique_ptr<SwHTMLTableLayoutColumn> CreateLayoutInfo();
379 // HTML table
380 typedef std::vector<SdrObject *> SdrObjects;
382 class HTMLTable
384 OUString m_aId;
385 OUString m_aStyle;
386 OUString m_aClass;
387 OUString m_aDir;
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
392 std::vector<HTMLTableRow> m_aRows; ///< table rows
393 std::vector<HTMLTableColumn> m_aColumns; ///< table columns
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
399 sal_uInt16 m_nCurrentRow; // current Row
400 sal_uInt16 m_nCurrentColumn; // current Column
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)
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;
410 sal_uInt16 m_nBoxes; // number of boxes in the table
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 %
450 SwHTMLParser *m_pParser; // the current parser
451 std::unique_ptr<HTMLTableCnts> m_xParentContents;
453 std::unique_ptr<HTMLTableContext> m_pContext; // the context of the table
455 std::shared_ptr<SwHTMLTableLayout> m_xLayoutInfo;
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
467 void InitCtor(const HTMLTableOptions& rOptions);
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 );
472 // Protects the chosen cell and the cells among
473 void ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan );
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;
479 sal_uInt16 GetTopCellSpace( sal_uInt16 nRow ) const;
480 sal_uInt16 GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const;
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;
487 // Create a table with the content (lines/boxes)
488 void MakeTable_( SwTableBox *pUpper );
490 // Generate a new SwTableBox, which contains a SwStartNode
491 SwTableBox *NewTableBox( const SwStartNode *pStNd,
492 SwTableLine *pUpper ) const;
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 );
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 );
506 // Autolayout-Algorithm
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 );
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 );
518 // Set the border with the help of the information from the user
519 void SetBorders();
521 // is the border already set?
522 bool BordersSet() const { return m_bBordersSet; }
524 const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBackgroundBrush; }
525 const std::unique_ptr<SvxBrushItem>& GetInhBGBrush() const { return m_xInheritedBackgroundBrush; }
527 sal_uInt16 GetBorderWidth( const SvxBorderLine& rBLine,
528 bool bWithDistance=false ) const;
530 public:
532 bool m_bFirstCell; // is there a cell created already?
534 HTMLTable(SwHTMLParser* pPars,
535 bool bParHead, bool bHasParentSec,
536 bool bHasToFly,
537 const HTMLTableOptions& rOptions);
539 ~HTMLTable();
541 // Identifying of a cell
542 const HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const;
543 HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell)
545 return const_cast<HTMLTableCell&>(const_cast<const HTMLTable&>(*this).GetCell(nRow, nCell));
548 // set/determine caption
549 inline void SetCaption( const SwStartNode *pStNd, bool bTop );
550 const SwStartNode *GetCaptionStartNode() const { return m_pCaptionStartNode; }
551 bool IsTopCaption() const { return m_bTopCaption; }
553 SvxAdjust GetTableAdjust( bool bAny ) const
555 return (m_bTableAdjustOfTag || bAny) ? m_eTableAdjust : SvxAdjust::End;
558 sal_uInt16 GetHSpace() const { return m_nHSpace; }
559 sal_uInt16 GetVSpace() const { return m_nVSpace; }
561 // get inherited drawing::Alignment of rows and column
562 SvxAdjust GetInheritedAdjust() const;
563 sal_Int16 GetInheritedVertOri() const;
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 );
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 );
577 // announce the end of a new section
578 inline void CloseSection( bool bHead );
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 );
584 // insert a new column
585 void InsertCol( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
586 SvxAdjust eAdjust, sal_Int16 eVertOri );
588 // End a table definition (needs to be called for every table)
589 void CloseTable();
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 );
596 bool IsNewDoc() const { return m_pParser->IsNewDoc(); }
598 void SetHasParentSection( bool bSet ) { m_bHasParentSection = bSet; }
599 bool HasParentSection() const { return m_bHasParentSection; }
601 void SetParentContents(std::unique_ptr<HTMLTableCnts> pCnts) { m_xParentContents = std::move(pCnts); }
602 std::unique_ptr<HTMLTableCnts>& GetParentContents() { return m_xParentContents; }
604 void MakeParentContents();
606 bool HasToFly() const { return m_bHasToFly; }
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 );
612 HTMLTableContext *GetContext() const { return m_pContext.get(); }
614 const std::shared_ptr<SwHTMLTableLayout>& CreateLayoutInfo();
616 bool HasColTags() const { return m_bColSpec; }
618 sal_uInt16 IncGrfsThatResize() { return m_pSwTable ? const_cast<SwTable *>(m_pSwTable)->IncGrfsThatResize() : 0; }
620 void RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth );
622 const SwTable *GetSwTable() const { return m_pSwTable; }
624 void SetBGBrush(const SvxBrushItem& rBrush) { m_xBackgroundBrush.reset(new SvxBrushItem(rBrush)); }
626 const OUString& GetId() const { return m_aId; }
627 const OUString& GetClass() const { return m_aClass; }
628 const OUString& GetStyle() const { return m_aStyle; }
629 const OUString& GetDirection() const { return m_aDir; }
631 void IncBoxCount() { m_nBoxes++; }
632 bool IsOverflowing() const { return m_nBoxes > 64000; }
634 bool PendingDrawObjectsInPaM(SwPaM& rPam) const;
637 void HTMLTableCnts::InitCtor()
639 m_pNext = nullptr;
640 m_xLayoutInfo.reset();
641 m_bNoBreak = false;
644 HTMLTableCnts::HTMLTableCnts(const SwStartNode* pStNd)
645 : m_pStartNode(pStNd)
647 InitCtor();
650 HTMLTableCnts::HTMLTableCnts(const std::shared_ptr<HTMLTable>& rTab)
651 : m_pStartNode(nullptr)
652 , m_xTable(rTab)
654 InitCtor();
657 HTMLTableCnts::~HTMLTableCnts()
659 m_xTable.reset(); // we don't need the tables anymore
660 m_pNext.reset();
663 void HTMLTableCnts::Add( std::unique_ptr<HTMLTableCnts> pNewCnts )
665 HTMLTableCnts *pCnts = this;
667 while( pCnts->m_pNext )
668 pCnts = pCnts->m_pNext.get();
670 pCnts->m_pNext = std::move(pNewCnts);
673 inline void HTMLTableCnts::SetTableBox( SwTableBox *pBox )
675 OSL_ENSURE(m_xLayoutInfo, "There is no layout info");
676 if (m_xLayoutInfo)
677 m_xLayoutInfo->SetTableBox(pBox);
680 const std::shared_ptr<SwHTMLTableLayoutCnts>& HTMLTableCnts::CreateLayoutInfo()
682 if (!m_xLayoutInfo)
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);
693 return m_xLayoutInfo;
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)
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 )
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;
725 m_bHasNumFormat = bHasNF;
726 m_bHasValue = bHasV;
727 m_nNumFormat = nNF;
728 m_nValue = nVal;
730 m_bNoWrap = bNWrap;
731 mbCovered = bCovered;
734 inline void HTMLTableCell::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
736 m_nWidth = nWdth;
737 m_bRelWidth = bRelWdth;
740 void HTMLTableCell::SetProtected()
742 // The content of this cell doesn't have to be anchored anywhere else,
743 // since they're not gonna be deleted
745 m_xContents.reset();
747 // Copy background color
748 if (m_xBGBrush)
749 m_xBGBrush = std::make_shared<SvxBrushItem>(*m_xBGBrush);
751 m_nRowSpan = 1;
752 m_nColSpan = 1;
753 m_bProtected = true;
756 inline bool HTMLTableCell::GetNumFormat( sal_uInt32& rNumFormat ) const
758 rNumFormat = m_nNumFormat;
759 return m_bHasNumFormat;
762 inline bool HTMLTableCell::GetValue( double& rValue ) const
764 rValue = m_nValue;
765 return m_bHasValue;
768 std::unique_ptr<SwHTMLTableLayoutCell> HTMLTableCell::CreateLayoutInfo()
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));
777 HTMLTableRow::HTMLTableRow(sal_uInt16 const nCells)
778 : m_aCells(nCells)
779 , m_eAdjust(SvxAdjust::End)
780 , m_nHeight(0)
781 , m_nEmptyRows(0)
782 , m_eVertOri(text::VertOrientation::TOP)
783 , m_bIsEndOfGroup(false)
784 , m_bBottomBorder(false)
786 assert(nCells == m_aCells.size() &&
787 "wrong Cell count in new HTML table row");
790 inline void HTMLTableRow::SetHeight( sal_uInt16 nHght )
792 if( nHght > m_nHeight )
793 m_nHeight = nHght;
796 const HTMLTableCell& HTMLTableRow::GetCell(sal_uInt16 nCell) const
798 OSL_ENSURE( nCell < m_aCells.size(),
799 "invalid cell index in HTML table row" );
800 return m_aCells.at(nCell);
803 void HTMLTableRow::Expand( sal_uInt16 nCells, bool bOneCell )
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!
808 sal_uInt16 nColSpan = nCells - m_aCells.size();
809 for (sal_uInt16 i = m_aCells.size(); i < nCells; ++i)
811 m_aCells.emplace_back();
812 if (bOneCell)
813 m_aCells.back().SetColSpan(nColSpan);
814 --nColSpan;
817 OSL_ENSURE(nCells == m_aCells.size(),
818 "wrong Cell count in expanded HTML table row");
821 void HTMLTableRow::Shrink( sal_uInt16 nCells )
823 OSL_ENSURE(nCells < m_aCells.size(), "number of cells too large");
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 )
833 HTMLTableCell& rCell = m_aCells[--i];
834 if (!rCell.GetContents())
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);
842 else
843 break;
845 #if OSL_DEBUG_LEVEL > 0
846 for( i=nCells; i<nEnd; i++ )
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" );
855 #endif
857 m_aCells.erase(m_aCells.begin() + nCells, m_aCells.end());
860 HTMLTableColumn::HTMLTableColumn():
861 m_bIsEndOfGroup(false),
862 m_nWidth(0), m_bRelWidth(false),
863 m_eAdjust(SvxAdjust::End), m_eVertOri(text::VertOrientation::TOP),
864 m_bLeftBorder(false)
866 for(SwFrameFormat* & rp : m_aFrameFormats)
867 rp = nullptr;
870 inline void HTMLTableColumn::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
872 if( m_bRelWidth==bRelWdth )
874 if( nWdth > m_nWidth )
875 m_nWidth = nWdth;
877 else
878 m_nWidth = nWdth;
879 m_bRelWidth = bRelWdth;
882 inline std::unique_ptr<SwHTMLTableLayoutColumn> HTMLTableColumn::CreateLayoutInfo()
884 return std::unique_ptr<SwHTMLTableLayoutColumn>(new SwHTMLTableLayoutColumn( m_nWidth, m_bRelWidth, m_bLeftBorder ));
887 inline sal_uInt16 HTMLTableColumn::GetFrameFormatIdx( bool bBorderLine,
888 sal_Int16 eVertOrient )
890 OSL_ENSURE( text::VertOrientation::TOP != eVertOrient, "Top is not allowed" );
891 sal_uInt16 n = bBorderLine ? 3 : 0;
892 switch( eVertOrient )
894 case text::VertOrientation::CENTER: n+=1; break;
895 case text::VertOrientation::BOTTOM: n+=2; break;
896 default:
899 return n;
902 inline void HTMLTableColumn::SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
903 sal_Int16 eVertOrient )
905 m_aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)] = pFormat;
908 inline SwFrameFormat *HTMLTableColumn::GetFrameFormat( bool bBorderLine,
909 sal_Int16 eVertOrient ) const
911 return m_aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)];
914 void HTMLTable::InitCtor(const HTMLTableOptions& rOptions)
916 m_nRows = 0;
917 m_nCurrentRow = 0; m_nCurrentColumn = 0;
919 m_pBoxFormat = nullptr; m_pLineFormat = nullptr;
920 m_pLineFrameFormatNoHeight = nullptr;
921 m_xInheritedBackgroundBrush.reset();
923 m_pPrevStartNode = nullptr;
924 m_pSwTable = nullptr;
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;
934 m_nLeftMargin = 0;
935 m_nRightMargin = 0;
937 const Color& rBorderColor = rOptions.aBorderColor;
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 );
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;
952 if ( rOptions.nCellSpacing != 0 )
954 m_aTopBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
956 m_aTopBorderLine.SetWidth( nPHeight );
957 m_aTopBorderLine.SetColor( rBorderColor );
958 m_aBottomBorderLine = m_aTopBorderLine;
960 if( nPWidth == nPHeight )
962 m_aLeftBorderLine = m_aTopBorderLine;
964 else
966 if ( rOptions.nCellSpacing != 0 )
968 m_aLeftBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
970 m_aLeftBorderLine.SetWidth( nPWidth );
971 m_aLeftBorderLine.SetColor( rBorderColor );
973 m_aRightBorderLine = m_aLeftBorderLine;
975 if( rOptions.nCellSpacing != 0 )
977 m_aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
978 m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 );
980 else
982 m_aBorderLine.SetWidth( DEF_LINE_WIDTH_0 );
984 m_aBorderLine.SetColor( rBorderColor );
986 if( m_nCellPadding )
988 if( m_nCellPadding==USHRT_MAX )
989 m_nCellPadding = MIN_BORDER_DIST; // default
990 else
992 m_nCellPadding = SwHTMLParser::ToTwips( m_nCellPadding );
993 if( m_nCellPadding<MIN_BORDER_DIST )
994 m_nCellPadding = MIN_BORDER_DIST;
997 if( m_nCellSpacing )
999 if( m_nCellSpacing==USHRT_MAX )
1000 m_nCellSpacing = NETSCAPE_DFLT_CELLSPACING;
1001 m_nCellSpacing = SwHTMLParser::ToTwips( m_nCellSpacing );
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);
1010 m_bColSpec = false;
1012 m_xBackgroundBrush.reset(m_pParser->CreateBrushItem(
1013 rOptions.bBGColor ? &(rOptions.aBGColor) : nullptr,
1014 rOptions.aBGImage, OUString(), OUString(), OUString()));
1016 m_pContext = nullptr;
1017 m_xParentContents.reset();
1019 m_aId = rOptions.aId;
1020 m_aClass = rOptions.aClass;
1021 m_aStyle = rOptions.aStyle;
1022 m_aDir = rOptions.aDir;
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)
1052 InitCtor(rOptions);
1053 m_pParser->RegisterHTMLTable(this);
1056 void SwHTMLParser::DeregisterHTMLTable(HTMLTable* pOld)
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));
1063 SwDoc* SwHTMLParser::GetDoc() const
1065 return m_xDoc.get();
1068 bool SwHTMLParser::IsReqIF() const
1070 return m_bReqIF;
1073 HTMLTable::~HTMLTable()
1075 m_pParser->DeregisterHTMLTable(this);
1077 m_xResizeDrawObjects.reset();
1078 m_xDrawObjectPercentWidths.reset();
1080 m_pContext.reset();
1082 // pLayoutInfo has either already been deleted or is now owned by SwTable
1085 const std::shared_ptr<SwHTMLTableLayout>& HTMLTable::CreateLayoutInfo()
1087 sal_uInt16 nW = m_bPercentWidth ? m_nWidth : SwHTMLParser::ToTwips( m_nWidth );
1089 sal_uInt16 nBorderWidth = GetBorderWidth( m_aBorderLine, true );
1090 sal_uInt16 nLeftBorderWidth =
1091 m_aColumns[0].m_bLeftBorder ? GetBorderWidth(m_aLeftBorderLine, true) : 0;
1092 sal_uInt16 nRightBorderWidth =
1093 m_bRightBorder ? GetBorderWidth( m_aRightBorderLine, true ) : 0;
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);
1103 bool bExportable = true;
1104 sal_uInt16 i;
1105 for( i=0; i<m_nRows; i++ )
1107 HTMLTableRow& rRow = m_aRows[i];
1108 for( sal_uInt16 j=0; j<m_nCols; j++ )
1110 m_xLayoutInfo->SetCell(rRow.GetCell(j).CreateLayoutInfo(), i, j);
1111 SwHTMLTableLayoutCell* pLayoutCell = m_xLayoutInfo->GetCell(i, j );
1113 if( bExportable )
1115 const std::shared_ptr<SwHTMLTableLayoutCnts>& rLayoutCnts =
1116 pLayoutCell->GetContents();
1117 bExportable = !rLayoutCnts ||
1118 (rLayoutCnts->GetStartNode() && !rLayoutCnts->GetNext());
1123 m_xLayoutInfo->SetExportable( bExportable );
1125 for( i=0; i<m_nCols; i++ )
1126 m_xLayoutInfo->SetColumn(m_aColumns[i].CreateLayoutInfo(), i);
1128 return m_xLayoutInfo;
1131 inline void HTMLTable::SetCaption( const SwStartNode *pStNd, bool bTop )
1133 m_pCaptionStartNode = pStNd;
1134 m_bTopCaption = bTop;
1137 void HTMLTable::FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol,
1138 const HTMLTableCnts *pCnts )
1140 sal_uInt16 nRowSpan=1;
1141 while (true)
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);
1150 if( !nRow ) break;
1151 nRowSpan++; nRow--;
1155 void HTMLTable::ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan )
1157 for( sal_uInt16 i=0; i<nRowSpan; i++ )
1159 GetCell(nRow+i,nCol).SetProtected();
1160 if (m_xLayoutInfo)
1161 m_xLayoutInfo->GetCell(nRow+i,nCol)->SetProtected();
1165 // Search the SwStartNode of the last used predecessor box
1166 const SwStartNode* HTMLTable::GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCol ) const
1168 const HTMLTableCnts *pPrevCnts = nullptr;
1170 if( 0==nRow )
1172 // always the predecessor cell
1173 if( nCol>0 )
1174 pPrevCnts = GetCell(0, nCol - 1).GetContents().get();
1175 else
1176 return m_pPrevStartNode;
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
1183 sal_uInt16 i;
1184 const HTMLTableRow& rPrevRow = m_aRows[nRow-1];
1186 // maybe a cell in the current row
1187 i = nCol;
1188 while( i )
1190 i--;
1191 if( 1 == rPrevRow.GetCell(i).GetRowSpan() )
1193 pPrevCnts = GetCell(nRow, i).GetContents().get();
1194 break;
1198 // otherwise the last filled cell of the row before
1199 if( !pPrevCnts )
1201 i = m_nCols;
1202 while( !pPrevCnts && i )
1204 i--;
1205 pPrevCnts = rPrevRow.GetCell(i).GetContents().get();
1209 OSL_ENSURE( pPrevCnts, "No previous filled cell found" );
1210 if( !pPrevCnts )
1212 pPrevCnts = GetCell(0, 0).GetContents().get();
1213 if( !pPrevCnts )
1214 return m_pPrevStartNode;
1217 while( pPrevCnts->Next() )
1218 pPrevCnts = pPrevCnts->Next();
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);
1229 sal_uInt16 HTMLTable::GetTopCellSpace( sal_uInt16 nRow ) const
1231 sal_uInt16 nSpace = m_nCellPadding;
1233 if( nRow == 0 )
1235 nSpace += m_nBorder + m_nCellSpacing;
1238 return nSpace;
1241 sal_uInt16 HTMLTable::GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const
1243 sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
1245 if( nRow+nRowSpan == m_nRows )
1247 nSpace = nSpace + m_nBorder;
1250 return nSpace;
1253 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
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;
1270 const HTMLTableColumn& rColumn = m_aColumns[nCol];
1272 if( pBox->GetSttNd() )
1274 // Determine background color/graphic
1275 const HTMLTableCell& rCell = GetCell(nRow, nCol);
1276 pBoxItem = rCell.GetBoxItem();
1277 pBGBrushItem = rCell.GetBGBrush().get();
1278 if( !pBGBrushItem )
1280 // If a cell spans multiple rows, a background to that row should be copied to the cell.
1281 if (nRowSpan > 1)
1283 pBGBrushItem = m_aRows[nRow].GetBGBrush().get();
1287 bTopLine = 0==nRow && m_bTopBorder && bFirstPara;
1288 if (m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
1290 nEmptyRows = m_aRows[nRow+nRowSpan-1].GetEmptyRows();
1291 if( nRow+nRowSpan == m_nRows )
1292 bLastBottomLine = true;
1293 else
1294 bBottomLine = true;
1297 eVOri = rCell.GetVertOri();
1298 bHasNumFormat = rCell.GetNumFormat( nNumFormat );
1299 if( bHasNumFormat )
1300 bHasValue = rCell.GetValue( nValue );
1302 if( nColSpan==1 && !bTopLine && !bLastBottomLine && !nEmptyRows &&
1303 !pBGBrushItem && !bHasNumFormat && !pBoxItem)
1305 pFrameFormat = rColumn.GetFrameFormat( bBottomLine, eVOri );
1306 bReUsable = !pFrameFormat;
1310 if( !pFrameFormat )
1312 pFrameFormat = pBox->ClaimFrameFormat();
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());
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() )
1326 bool bSet = (m_nCellPadding > 0);
1328 SvxBoxItem aBoxItem( RES_BOX );
1329 tools::Long nInnerFrameWidth = nFrameWidth;
1331 if( bTopLine )
1333 aBoxItem.SetLine( &m_aTopBorderLine, SvxBoxItemLine::TOP );
1334 bSet = true;
1336 if( bLastBottomLine )
1338 aBoxItem.SetLine( &m_aBottomBorderLine, SvxBoxItemLine::BOTTOM );
1339 bSet = true;
1341 else if( bBottomLine )
1343 if( nEmptyRows && !m_aBorderLine.GetInWidth() )
1345 // For now, empty rows can only be emulated by thick lines, if it's a single line
1346 SvxBorderLine aThickBorderLine( m_aBorderLine );
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 );
1354 else
1356 aBoxItem.SetLine( &m_aBorderLine, SvxBoxItemLine::BOTTOM );
1358 bSet = true;
1360 if (m_aColumns[nCol].m_bLeftBorder)
1362 const SvxBorderLine& rBorderLine =
1363 0==nCol ? m_aLeftBorderLine : m_aBorderLine;
1364 aBoxItem.SetLine( &rBorderLine, SvxBoxItemLine::LEFT );
1365 nInnerFrameWidth -= GetBorderWidth( rBorderLine );
1366 bSet = true;
1368 if( m_bRightBorder )
1370 aBoxItem.SetLine( &m_aRightBorderLine, SvxBoxItemLine::RIGHT );
1371 nInnerFrameWidth -= GetBorderWidth( m_aRightBorderLine );
1372 bSet = true;
1375 if (pBoxItem)
1377 pFrameFormat->SetFormatAttr( *pBoxItem );
1379 else if (bSet)
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 );
1390 else
1391 pFrameFormat->ResetFormatAttr( RES_BOX );
1393 if( pBGBrushItem )
1395 pFrameFormat->SetFormatAttr( *pBGBrushItem );
1397 else
1398 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1400 // Only set format if there's a value or the box is empty
1401 if( bHasNumFormat && (bHasValue || pBox->IsEmpty()) )
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 )
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 ) )
1419 eAdjust = static_cast<const SvxAdjustItem *>(pItem)
1420 ->GetAdjust();
1423 aItemSet.Put( SwTableBoxNumFormat(nNumFormat) );
1424 if( bHasValue )
1425 aItemSet.Put( SwTableBoxValue(nValue) );
1427 if( bLock )
1428 pFrameFormat->LockModify();
1429 pFrameFormat->SetFormatAttr( aItemSet );
1430 if( bLock )
1431 pFrameFormat->UnlockModify();
1432 else if( pCNd && SvxAdjust::End != eAdjust )
1434 SvxAdjustItem aAdjItem( eAdjust, RES_PARATR_ADJUST );
1435 pCNd->SetAttr( aAdjItem );
1438 else
1439 pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1441 OSL_ENSURE( eVOri != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
1442 if( text::VertOrientation::NONE != eVOri )
1444 pFrameFormat->SetFormatAttr( SwFormatVertOrient( 0, eVOri ) );
1446 else
1447 pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
1449 if( bReUsable )
1450 const_cast<HTMLTableColumn&>(rColumn).SetFrameFormat(pFrameFormat, bBottomLine, eVOri);
1452 else
1454 pFrameFormat->ResetFormatAttr( RES_BOX );
1455 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1456 pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
1457 pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1460 if (m_pParser->IsReqIF())
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)
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());
1479 else
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) );
1490 SwTableBox *HTMLTable::NewTableBox( const SwStartNode *pStNd,
1491 SwTableLine *pUpper ) const
1493 SwTableBox *pBox;
1495 if (m_xBox1 && m_xBox1->GetSttNd() == pStNd)
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);
1501 else
1502 pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper );
1504 return pBox;
1507 static void ResetLineFrameFormatAttrs( SwFrameFormat *pFrameFormat )
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" );
1516 // !!! could be simplified
1517 SwTableLine *HTMLTable::MakeTableLine( SwTableBox *pUpper,
1518 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
1519 sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
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 );
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)
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();
1538 if( nTopRow==nBottomRow-1 && (nRowHeight || pBGBrushItem) )
1540 SwTableLineFormat *pFrameFormat = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1541 ResetLineFrameFormatAttrs( pFrameFormat );
1543 if( nRowHeight )
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 );
1550 pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, nRowHeight ) );
1553 if( pBGBrushItem )
1555 pFrameFormat->SetFormatAttr( *pBGBrushItem );
1559 else if( !m_pLineFrameFormatNoHeight )
1561 // else, we'll have to remove the height from the attribute and remember the format
1562 m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1564 ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
1567 SwTableBoxes& rBoxes = pLine->GetTabBoxes();
1569 sal_uInt16 nStartCol = nLeftCol;
1570 while( nStartCol<nRightCol )
1572 sal_uInt16 nCol = nStartCol;
1573 sal_uInt16 nSplitCol = nRightCol;
1574 bool bSplitted = false;
1575 while( !bSplitted )
1577 OSL_ENSURE( nCol < nRightCol, "Gone too far" );
1579 HTMLTableCell& rCell = GetCell(nTopRow,nCol);
1580 const bool bSplit = 1 == rCell.GetColSpan();
1582 OSL_ENSURE((nCol != nRightCol-1) || bSplit, "Split-Flag wrong");
1583 if( bSplit )
1585 SwTableBox* pBox = nullptr;
1586 HTMLTableCell& rCell2 = GetCell(nTopRow, nStartCol);
1587 if (rCell2.GetColSpan() == (nCol+1-nStartCol))
1589 // The HTML tables represent a box. So we need to split behind that box
1590 nSplitCol = nCol + 1;
1592 sal_Int32 nBoxRowSpan = rCell2.GetRowSpan();
1593 if (!rCell2.GetContents() || rCell2.IsCovered())
1595 if (rCell2.IsCovered())
1596 nBoxRowSpan = -1 * nBoxRowSpan;
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();
1605 rCell2.SetContents(xCnts);
1606 SwHTMLTableLayoutCell *pCurrCell = m_xLayoutInfo->GetCell(nTopRow, nStartCol);
1607 pCurrCell->SetContents(xCntsLayoutInfo);
1608 if( nBoxRowSpan < 0 )
1609 pCurrCell->SetRowSpan( 0 );
1611 // check COLSPAN if needed
1612 for( sal_uInt16 j=nStartCol+1; j<nSplitCol; j++ )
1614 GetCell(nTopRow, j).SetContents(xCnts);
1615 m_xLayoutInfo->GetCell(nTopRow, j)
1616 ->SetContents(xCntsLayoutInfo);
1620 pBox = MakeTableBox(pLine, rCell2.GetContents().get(),
1621 nTopRow, nStartCol,
1622 nBottomRow, nSplitCol);
1624 if (1 != nBoxRowSpan && pBox)
1625 pBox->setRowSpan( nBoxRowSpan );
1627 bSplitted = true;
1630 OSL_ENSURE( pBox, "Colspan trouble" );
1632 if( pBox )
1633 rBoxes.push_back( pBox );
1635 nCol++;
1637 nStartCol = nSplitCol;
1640 return pLine;
1643 SwTableBox *HTMLTable::MakeTableBox( SwTableLine *pUpper,
1644 HTMLTableCnts *pCnts,
1645 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
1646 sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
1648 SwTableBox *pBox;
1649 sal_uInt16 nColSpan = nRightCol - nLeftCol;
1650 sal_uInt16 nRowSpan = nBottomRow - nTopRow;
1652 if( !pCnts->Next() )
1654 // just one content section
1655 if( pCnts->GetStartNode() )
1657 // ... that's not a table
1658 pBox = NewTableBox( pCnts->GetStartNode(), pUpper );
1659 pCnts->SetTableBox( pBox );
1661 else if (HTMLTable* pTable = pCnts->GetTable().get())
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 );
1676 else
1678 return nullptr;
1681 else
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;
1688 while( pCnts )
1690 if( pCnts->GetStartNode() )
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 )
1698 // If there's no line format without height yet, we can use that one
1699 m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1701 ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
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 );
1711 rLines.push_back( pLine );
1713 else
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 );
1729 pCnts = pCnts->Next();
1730 bFirstPara = false;
1734 FixFrameFormat( pBox, nTopRow, nLeftCol, nRowSpan, nColSpan );
1736 return pBox;
1739 void HTMLTable::InheritBorders( const HTMLTable *pParent,
1740 sal_uInt16 nRow, sal_uInt16 nCol,
1741 sal_uInt16 nRowSpan,
1742 bool bFirstPara, bool bLastPara )
1744 OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
1745 "Was CloseTable not called?" );
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
1752 if( 0==nRow && pParent->m_bTopBorder && bFirstPara )
1754 m_bTopBorder = true;
1755 m_bFillerTopBorder = true; // fillers get a border too
1756 m_aTopBorderLine = pParent->m_aTopBorderLine;
1758 if (pParent->m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
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;
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())) );
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 )
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();
1785 if( pInhBG )
1786 m_xInheritedBackgroundBrush.reset(new SvxBrushItem(*pInhBG));
1789 void HTMLTable::InheritVertBorders( const HTMLTable *pParent,
1790 sal_uInt16 nCol, sal_uInt16 nColSpan )
1792 sal_uInt16 nInhLeftBorderWidth = 0;
1793 sal_uInt16 nInhRightBorderWidth = 0;
1795 if( nCol+nColSpan==pParent->m_nCols && pParent->m_bRightBorder )
1797 m_bInheritedRightBorder = true; // just remember for now
1798 m_aInheritedRightBorderLine = pParent->m_aRightBorderLine;
1799 nInhRightBorderWidth =
1800 GetBorderWidth( m_aInheritedRightBorderLine, true ) + MIN_BORDER_DIST;
1803 if (pParent->m_aColumns[nCol].m_bLeftBorder)
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;
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 );
1819 m_bRightAllowed = ( pParent->m_bRightAllowed &&
1820 (nCol+nColSpan==pParent->m_nCols ||
1821 !pParent->m_aColumns[nCol+nColSpan].m_bLeftBorder) );
1824 void HTMLTable::SetBorders()
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()))
1832 m_aColumns[i].m_bLeftBorder = true;
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()))
1840 m_aRows[i].SetBottomBorder(true);
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 )
1849 m_aRows[m_nRows-1].SetBottomBorder(true);
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 )
1856 m_aColumns[0].m_bLeftBorder = true;
1859 for( i=0; i<m_nRows; i++ )
1861 HTMLTableRow& rRow = m_aRows[i];
1862 for (sal_uInt16 j=0; j<m_nCols; ++j)
1864 HTMLTableCell& rCell = rRow.GetCell(j);
1865 if (rCell.GetContents())
1867 HTMLTableCnts *pCnts = rCell.GetContents().get();
1868 bool bFirstPara = true;
1869 while( pCnts )
1871 HTMLTable *pTable = pCnts->GetTable().get();
1872 if( pTable && !pTable->BordersSet() )
1874 pTable->InheritBorders(this, i, j,
1875 rCell.GetRowSpan(),
1876 bFirstPara,
1877 nullptr==pCnts->Next());
1878 pTable->SetBorders();
1880 bFirstPara = false;
1881 pCnts = pCnts->Next();
1887 m_bBordersSet = true;
1890 sal_uInt16 HTMLTable::GetBorderWidth( const SvxBorderLine& rBLine,
1891 bool bWithDistance ) const
1893 sal_uInt16 nBorderWidth = rBLine.GetWidth();
1894 if( bWithDistance )
1896 if( m_nCellPadding )
1897 nBorderWidth = nBorderWidth + m_nCellPadding;
1898 else if( nBorderWidth )
1899 nBorderWidth = nBorderWidth + MIN_BORDER_DIST;
1902 return nBorderWidth;
1905 const HTMLTableCell& HTMLTable::GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const
1907 OSL_ENSURE(nRow < m_aRows.size(), "invalid row index in HTML table");
1908 return m_aRows[nRow].GetCell(nCell);
1911 SvxAdjust HTMLTable::GetInheritedAdjust() const
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();
1918 return eAdjust;
1921 sal_Int16 HTMLTable::GetInheritedVertOri() const
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;
1930 OSL_ENSURE( m_eVertOrientation != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
1931 return eVOri;
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 )
1942 if( !nRowSpan || static_cast<sal_uInt32>(m_nCurrentRow) + nRowSpan > USHRT_MAX )
1943 nRowSpan = 1;
1945 if( !nColSpan || static_cast<sal_uInt32>(m_nCurrentColumn) + nColSpan > USHRT_MAX )
1946 nColSpan = 1;
1948 sal_uInt16 nColsReq = m_nCurrentColumn + nColSpan;
1949 sal_uInt16 nRowsReq = m_nCurrentRow + nRowSpan;
1950 sal_uInt16 i, j;
1952 // if we need more columns than we currently have, we need to add cells for all rows
1953 if( m_nCols < nColsReq )
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");
1962 if( nColsReq > m_nFilledColumns )
1963 m_nFilledColumns = nColsReq;
1965 // if we need more rows than we currently have, we need to add cells
1966 if( m_nRows < nRowsReq )
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");
1974 // Check if we have an overlap and could remove that
1975 sal_uInt16 nSpanedCols = 0;
1976 if( m_nCurrentRow>0 )
1978 HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
1979 for( i=m_nCurrentColumn; i<nColsReq; i++ )
1981 HTMLTableCell& rCell = rCurRow.GetCell(i);
1982 if (rCell.GetContents())
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 );
1994 for( i=nColsReq; i<nSpanedCols; i++ )
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() );
2003 // Fill the cells
2004 for( i=nColSpan; i>0; i-- )
2006 for( j=nRowSpan; j>0; j-- )
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 );
2015 Size aTwipSz( bRelWidth ? 0 : nCellWidth, nCellHeight );
2016 if( (aTwipSz.Width() || aTwipSz.Height()) && Application::GetDefaultDevice() )
2018 aTwipSz = Application::GetDefaultDevice()
2019 ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) );
2022 // Only set width on the first cell!
2023 if( nCellWidth )
2025 sal_uInt16 nTmp = bRelWidth ? nCellWidth : o3tl::narrowing<sal_uInt16>(aTwipSz.Width());
2026 GetCell( m_nCurrentRow, m_nCurrentColumn ).SetWidth( nTmp, bRelWidth );
2029 // Remember height
2030 if( nCellHeight && 1==nRowSpan )
2032 m_aRows[m_nCurrentRow].SetHeight(o3tl::narrowing<sal_uInt16>(aTwipSz.Height()));
2035 // Set the column counter behind the new cells
2036 m_nCurrentColumn = nColsReq;
2037 if( nSpanedCols > m_nCurrentColumn )
2038 m_nCurrentColumn = nSpanedCols;
2040 // and search for the next free cell
2041 while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
2042 m_nCurrentColumn++;
2045 inline void HTMLTable::CloseSection( bool bHead )
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;
2055 void HTMLTable::OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOrient,
2056 std::unique_ptr<SvxBrushItem>& rBGBrushItem)
2058 sal_uInt16 nRowsReq = m_nCurrentRow+1;
2060 // create the next row if it's not there already
2061 if( m_nRows<nRowsReq )
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" );
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);
2076 // reset the column counter
2077 m_nCurrentColumn=0;
2079 // and search for the next free cell
2080 while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
2081 m_nCurrentColumn++;
2084 void HTMLTable::CloseRow( bool bEmpty )
2086 OSL_ENSURE( m_nCurrentRow<m_nRows, "current row after table end" );
2088 // empty cells just get a slightly thicker lower border!
2089 if( bEmpty )
2091 if( m_nCurrentRow > 0 )
2092 m_aRows[m_nCurrentRow-1].IncEmptyRows();
2093 return;
2096 HTMLTableRow& rRow = m_aRows[m_nCurrentRow];
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 )
2103 HTMLTableCell& rCell = rRow.GetCell(--i);
2104 if (!rCell.GetContents())
2106 sal_uInt16 nColSpan = m_nCols-i;
2107 if( nColSpan > 1 )
2108 rCell.SetColSpan(nColSpan);
2110 else
2111 break;
2114 m_nCurrentRow++;
2117 inline void HTMLTable::CloseColGroup( sal_uInt16 nSpan, sal_uInt16 _nWidth,
2118 bool bRelWidth, SvxAdjust eAdjust,
2119 sal_Int16 eVertOrient )
2121 if( nSpan )
2122 InsertCol( nSpan, _nWidth, bRelWidth, eAdjust, eVertOrient );
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();
2129 void HTMLTable::InsertCol( sal_uInt16 nSpan, sal_uInt16 nColWidth, bool bRelWidth,
2130 SvxAdjust eAdjust, sal_Int16 eVertOrient )
2132 // #i35143# - no columns, if rows already exist.
2133 if ( m_nRows > 0 )
2134 return;
2136 sal_uInt16 i;
2138 if( !nSpan )
2139 nSpan = 1;
2141 sal_uInt16 nColsReq = m_nCurrentColumn + nSpan;
2143 if( m_nCols < nColsReq )
2145 m_aColumns.resize(nColsReq);
2146 m_nCols = nColsReq;
2149 Size aTwipSz( bRelWidth ? 0 : nColWidth, 0 );
2150 if( aTwipSz.Width() && Application::GetDefaultDevice() )
2152 aTwipSz = Application::GetDefaultDevice()
2153 ->PixelToLogic( aTwipSz, MapMode( MapUnit::MapTwip ) );
2156 for( i=m_nCurrentColumn; i<nColsReq; i++ )
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 );
2165 m_bColSpec = true;
2167 m_nCurrentColumn = nColsReq;
2170 void HTMLTable::CloseTable()
2172 sal_uInt16 i;
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 )
2179 HTMLTableRow& rPrevRow = m_aRows[m_nCurrentRow-1];
2180 for( i=0; i<m_nCols; i++ )
2182 HTMLTableCell& rCell = rPrevRow.GetCell(i);
2183 if (rCell.GetRowSpan() > 1)
2185 FixRowSpan(m_nCurrentRow-1, i, rCell.GetContents().get());
2186 ProtectRowSpan(m_nCurrentRow, i, m_aRows[m_nCurrentRow].GetCell(i).GetRowSpan());
2189 for( i=m_nRows-1; i>=m_nCurrentRow; i-- )
2190 m_aRows.erase(m_aRows.begin() + i);
2191 m_nRows = m_nCurrentRow;
2194 // if the table has no column, we need to add one
2195 if( 0==m_nCols )
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;
2204 // if the table has no row, we need to add one
2205 if( 0==m_nRows )
2207 m_aRows.emplace_back(m_nCols);
2208 m_nRows = 1;
2209 m_nCurrentRow = 1;
2212 if( m_nFilledColumns < m_nCols )
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;
2221 void HTMLTable::MakeTable_( SwTableBox *pBox )
2223 SwTableLines& rLines = (pBox ? pBox->GetTabLines()
2224 : const_cast<SwTable *>(m_pSwTable)->GetTabLines() );
2226 for( sal_uInt16 i=0; i<m_nRows; i++ )
2228 SwTableLine *pLine = MakeTableLine( pBox, i, 0, i+1, m_nCols );
2229 if( pBox || i > 0 )
2230 rLines.push_back( pLine );
2234 /* How are tables aligned?
2236 first row: without paragraph indents
2237 second row: with paragraph indents
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 %
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 %
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
2259 xxx *) if for the table no size was specified, always
2260 xxx text::HoriOrientation::FULL is taken
2264 void HTMLTable::MakeTable( SwTableBox *pBox, sal_uInt16 nAbsAvail,
2265 sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
2266 sal_uInt16 nAbsRightSpace, sal_uInt16 nInhAbsSpace )
2268 OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
2269 "Was CloseTable not called?" );
2271 OSL_ENSURE(m_xLayoutInfo == nullptr, "Table already has layout info");
2273 // Calculate borders of the table and all contained tables
2274 SetBorders();
2276 // Step 1: needed layout structures are created (including tables in tables)
2277 CreateLayoutInfo();
2279 if (!utl::ConfigManager::IsFuzzing()) // skip slow path for fuzzing
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();
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 );
2293 // Set adjustment for the top table
2294 sal_Int16 eHoriOri;
2295 if (m_bForceFrame)
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;
2301 else switch (m_eTableAdjust)
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
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;
2321 if (!m_pSwTable)
2323 SAL_WARN("sw.html", "no table");
2324 return;
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)
2332 OSL_ENSURE( m_nLeftMargin || m_nRightMargin,
2333 "There are still leftovers from relative margins" );
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 );
2342 if (m_bPercentWidth && text::HoriOrientation::FULL != eHoriOri)
2344 pFrameFormat->LockModify();
2345 SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
2346 aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidth) );
2347 pFrameFormat->SetFormatAttr( aFrameSize );
2348 pFrameFormat->UnlockModify();
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());
2357 m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat());
2358 m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_xBox1->GetFrameFormat());
2360 MakeTable_( pBox );
2362 // Finally, we'll do a garbage collection for the top level table
2364 if( 1==m_nRows && m_nHeight && 1==m_pSwTable->GetTabLines().size() )
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;
2373 (m_pSwTable->GetTabLines())[0]->ClaimFrameFormat();
2374 (m_pSwTable->GetTabLines())[0]->GetFrameFormat()
2375 ->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, m_nHeight ) );
2378 if( GetBGBrush() )
2379 m_pSwTable->GetFrameFormat()->SetFormatAttr( *GetBGBrush() );
2381 const_cast<SwTable *>(m_pSwTable)->SetRowsToRepeat( static_cast< sal_uInt16 >(m_nHeadlineRepeat) );
2382 const_cast<SwTable *>(m_pSwTable)->GCLines();
2384 bool bIsInFlyFrame = m_pContext && m_pContext->GetFrameFormat();
2385 if( bIsInFlyFrame && !m_nWidth )
2387 SvxAdjust eAdjust = GetTableAdjust(false);
2388 if (eAdjust != SvxAdjust::Left &&
2389 eAdjust != SvxAdjust::Right)
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;
2403 else
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() )
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;
2418 else
2420 m_xLayoutInfo->SetMustNotResize( true );
2424 m_xLayoutInfo->SetMayBeInFlyFrame( bIsInFlyFrame );
2426 // Only tables with relative width or without width should be modified
2427 m_xLayoutInfo->SetMustResize( m_bPercentWidth || !m_nWidth );
2429 if (!pLine1->GetTabBoxes().empty())
2430 m_xLayoutInfo->SetWidths();
2431 else
2432 SAL_WARN("sw.html", "no table box");
2434 const_cast<SwTable *>(m_pSwTable)->SetHTMLTableLayout(m_xLayoutInfo);
2436 if( !m_xResizeDrawObjects )
2437 return;
2439 sal_uInt16 nCount = m_xResizeDrawObjects->size();
2440 for( sal_uInt16 i=0; i<nCount; i++ )
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]);
2447 SwHTMLTableLayoutCell *pLayoutCell =
2448 m_xLayoutInfo->GetCell( nRow, nCol );
2449 sal_uInt16 nColSpan = pLayoutCell->GetColSpan();
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);
2455 SwHTMLParser::ResizeDrawObject( pObj, nWidth2 );
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 )
2464 m_pPrevStartNode = pStNd;
2465 m_pSwTable = pSwTab;
2466 m_pContext = std::move(pCntxt);
2468 m_nLeftMargin = nLeft;
2469 m_nRightMargin = nRight;
2471 m_bForceFrame = bFrcFrame;
2474 void HTMLTable::RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth )
2476 if( !m_xResizeDrawObjects )
2477 m_xResizeDrawObjects.emplace();
2478 m_xResizeDrawObjects->push_back( pObj );
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) );
2487 void HTMLTable::MakeParentContents()
2489 if( !GetContext() && !HasParentSection() )
2491 SetParentContents(
2492 m_pParser->InsertTableContents( m_bIsParentHead ) );
2494 SetHasParentSection( true );
2498 void HTMLTableContext::SavePREListingXMP( SwHTMLParser& rParser )
2500 m_bRestartPRE = rParser.IsReadPRE();
2501 m_bRestartXMP = rParser.IsReadXMP();
2502 m_bRestartListing = rParser.IsReadListing();
2503 rParser.FinishPREListingXMP();
2506 void HTMLTableContext::RestorePREListingXMP( SwHTMLParser& rParser )
2508 rParser.FinishPREListingXMP();
2510 if( m_bRestartPRE )
2511 rParser.StartPRE();
2513 if( m_bRestartXMP )
2514 rParser.StartXMP();
2516 if( m_bRestartListing )
2517 rParser.StartListing();
2520 const SwStartNode *SwHTMLParser::InsertTableSection
2521 ( const SwStartNode *pPrevStNd )
2523 OSL_ENSURE( pPrevStNd, "Start-Node is NULL" );
2525 m_pCSS1Parser->SetTDTagStyles();
2526 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TABLE );
2528 const SwStartNode *pStNd;
2529 if (m_xTable->m_bFirstCell )
2531 SwNode *const pNd = & m_pPam->GetPoint()->nNode.GetNode();
2532 pNd->GetTextNode()->ChgFormatColl( pColl );
2533 pStNd = pNd->FindTableBoxStartNode();
2534 m_xTable->m_bFirstCell = false;
2536 else if (pPrevStNd)
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();
2548 else
2550 eState = SvParserState::Error;
2551 return nullptr;
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 );
2563 return pStNd;
2566 const SwStartNode *SwHTMLParser::InsertTableSection( sal_uInt16 nPoolId )
2568 switch( nPoolId )
2570 case RES_POOLCOLL_TABLE_HDLN:
2571 m_pCSS1Parser->SetTHTagStyles();
2572 break;
2573 case RES_POOLCOLL_TABLE:
2574 m_pCSS1Parser->SetTDTagStyles();
2575 break;
2578 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( nPoolId );
2580 SwNode *const pNd = & m_pPam->GetPoint()->nNode.GetNode();
2581 const SwStartNode *pStNd;
2582 if (m_xTable->m_bFirstCell)
2584 SwTextNode* pTextNd = pNd->GetTextNode();
2585 if (!pTextNd)
2587 eState = SvParserState::Error;
2588 return nullptr;
2590 pTextNd->ChgFormatColl(pColl);
2591 m_xTable->m_bFirstCell = false;
2592 pStNd = pNd->FindTableBoxStartNode();
2594 else
2596 SwTableNode *pTableNd = pNd->FindTableNode();
2597 if (!pTableNd)
2599 eState = SvParserState::Error;
2600 return nullptr;
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() );
2611 SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
2612 pStNd = m_xDoc->GetNodes().MakeTextSection( aIdx, SwTableBoxStartNode,
2613 pColl );
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();
2621 if (!pStNd)
2623 eState = SvParserState::Error;
2626 return pStNd;
2629 SwStartNode *SwHTMLParser::InsertTempTableCaptionSection()
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 );
2637 rIdx = pStNd->GetIndex() + 1;
2638 m_pPam->GetPoint()->nContent.Assign( rIdx.GetNode().GetTextNode(), 0 );
2640 return pStNd;
2643 sal_Int32 SwHTMLParser::StripTrailingLF()
2645 sal_Int32 nStripped = 0;
2647 const sal_Int32 nLen = m_pPam->GetPoint()->nContent.GetIndex();
2648 if( nLen )
2650 SwTextNode* pTextNd = m_pPam->GetPoint()->nNode.GetNode().GetTextNode();
2651 // careful, when comments aren't ignored!!!
2652 if( pTextNd )
2654 sal_Int32 nPos = nLen;
2655 sal_Int32 nLFCount = 0;
2656 while (nPos && ('\x0a' == pTextNd->GetText()[--nPos]))
2657 nLFCount++;
2659 if( nLFCount )
2661 if( nLFCount > 2 )
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;
2672 nPos = nLen - nLFCount;
2673 SwIndex nIdx( pTextNd, nPos );
2674 pTextNd->EraseText( nIdx, nLFCount );
2675 nStripped = nLFCount;
2680 return nStripped;
2683 SvxBrushItem* SwHTMLParser::CreateBrushItem( const Color *pColor,
2684 const OUString& rImageURL,
2685 const OUString& rStyle,
2686 const OUString& rId,
2687 const OUString& rClass )
2689 SvxBrushItem *pBrushItem = nullptr;
2691 if( !rStyle.isEmpty() || !rId.isEmpty() || !rClass.isEmpty() )
2693 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), svl::Items<RES_BACKGROUND,
2694 RES_BACKGROUND>{} );
2695 SvxCSS1PropertyInfo aPropInfo;
2697 if( !rClass.isEmpty() )
2699 OUString aClass( rClass );
2700 SwCSS1Parser::GetScriptFromClass( aClass );
2701 const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass );
2702 if( pClass )
2703 aItemSet.Put( pClass->GetItemSet() );
2706 if( !rId.isEmpty() )
2708 const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId );
2709 if( pId )
2710 aItemSet.Put( pId->GetItemSet() );
2713 m_pCSS1Parser->ParseStyleOption( rStyle, aItemSet, aPropInfo );
2714 const SfxPoolItem *pItem = nullptr;
2715 if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false,
2716 &pItem ) )
2718 pBrushItem = new SvxBrushItem( *static_cast<const SvxBrushItem *>(pItem) );
2722 if( !pBrushItem && (pColor || !rImageURL.isEmpty()) )
2724 pBrushItem = new SvxBrushItem(RES_BACKGROUND);
2726 if( pColor )
2727 pBrushItem->SetColor(*pColor);
2729 if( !rImageURL.isEmpty() )
2731 pBrushItem->SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), rImageURL, Link<OUString *, bool>(), false) );
2732 pBrushItem->SetGraphicPos( GPOS_TILED );
2736 return pBrushItem;
2739 class SectionSaveStruct : public SwPendingData
2741 sal_uInt16 m_nBaseFontStMinSave, m_nFontStMinSave, m_nFontStHeadStartSave;
2742 sal_uInt16 m_nDefListDeepSave;
2743 size_t m_nContextStMinSave;
2744 size_t m_nContextStAttrMinSave;
2746 public:
2748 std::shared_ptr<HTMLTable> m_xTable;
2750 explicit SectionSaveStruct( SwHTMLParser& rParser );
2752 #if OSL_DEBUG_LEVEL > 0
2753 size_t GetContextStAttrMin() const { return m_nContextStAttrMinSave; }
2754 #endif
2755 void Restore( 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)
2766 // Freeze font stacks
2767 rParser.m_nBaseFontStMin = rParser.m_aBaseFontStack.size();
2769 rParser.m_nFontStMin = rParser.m_aFontStack.size();
2771 // Freeze context stack
2772 rParser.m_nContextStMin = rParser.m_aContexts.size();
2773 rParser.m_nContextStAttrMin = rParser.m_nContextStMin;
2775 // And remember a few counters
2776 rParser.m_nDefListDeep = 0;
2779 void SectionSaveStruct::Restore( SwHTMLParser& rParser )
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;
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;
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;
2801 // Reconstruct a few counters
2802 rParser.m_nDefListDeep = m_nDefListDeepSave;
2804 // Reset a few flags
2805 rParser.m_bNoParSpace = false;
2806 rParser.m_nOpenParaToken = HtmlTokenId::NONE;
2808 rParser.m_aParaAttrs.clear();
2811 class CellSaveStruct : public SectionSaveStruct
2813 OUString m_aStyle, m_aId, m_aClass;
2814 OUString m_aBGImage;
2815 Color m_aBGColor;
2816 std::shared_ptr<SvxBoxItem> m_xBoxItem;
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>
2822 double m_nValue;
2824 sal_uInt32 m_nNumFormat;
2826 sal_uInt16 m_nRowSpan, m_nColSpan, m_nWidth, m_nHeight;
2827 sal_Int32 m_nNoBreakEndContentPos; // Character index of a <NOBR>
2829 sal_Int16 m_eVertOri;
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
2839 public:
2841 CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable, bool bHd,
2842 bool bReadOpt );
2844 void AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts );
2845 bool HasFirstContents() const { return bool(m_xCnts); }
2847 void ClearIsInSection() { m_pCurrCnts = nullptr; }
2848 bool IsInSection() const { return m_pCurrCnts!=nullptr; }
2850 void InsertCell( SwHTMLParser& rParser, HTMLTable *pCurTable );
2852 bool IsHeaderCell() const { return m_bHead; }
2854 void StartNoBreak( const SwPosition& rPos );
2855 void EndNoBreak( const SwPosition& rPos );
2856 void CheckNoBreak( const SwPosition& rPos );
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 )
2879 OUString aNumFormat, aValue, aDir, aLang;
2880 SvxAdjust eAdjust( pCurTable->GetInheritedAdjust() );
2882 if( bReadOpt )
2884 const HTMLOptions& rOptions = rParser.GetOptions();
2885 for (size_t i = rOptions.size(); i; )
2887 const HTMLOption& rOption = rOptions[--i];
2888 switch( rOption.GetToken() )
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)
2897 SAL_INFO("sw.html", "ignoring huge COLSPAN " << m_nColSpan);
2898 m_nColSpan = 1;
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()))
2905 SAL_INFO("sw.html", "ignoring huge ROWSPAN " << m_nRowSpan);
2906 m_nRowSpan = 1;
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() )
2931 rOption.GetColor( m_aBGColor );
2932 m_bBGColor = true;
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;
2965 if( !m_aId.isEmpty() )
2966 rParser.InsertBookmark( m_aId );
2969 if( m_bHasNumFormat )
2971 LanguageType eLang;
2972 m_nValue = SfxHTMLParser::GetTableDataOptionsValNum(
2973 m_nNumFormat, eLang, aValue, aNumFormat,
2974 *rParser.m_xDoc->GetNumberFormatter() );
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 )
2983 nToken = HtmlTokenId::TABLEHEADER_ON;
2984 nColl = RES_POOLCOLL_TABLE_HDLN;
2986 else
2988 nToken = HtmlTokenId::TABLEDATA_ON;
2989 nColl = RES_POOLCOLL_TABLE;
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());
2996 if( SwHTMLParser::HasStyleOptions( m_aStyle, m_aId, m_aClass, &aLang, &aDir ) )
2998 SfxItemSet aItemSet( rParser.m_xDoc->GetAttrPool(),
2999 rParser.m_pCSS1Parser->GetWhichMap() );
3000 SvxCSS1PropertyInfo aPropInfo;
3002 if( rParser.ParseStyleOptions( m_aStyle, m_aId, m_aClass, aItemSet,
3003 aPropInfo, &aLang, &aDir ) )
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);
3011 rParser.InsertAttrs(aItemSet, aPropInfo, xCntxt.get());
3015 rParser.SplitPREListingXMP(xCntxt.get());
3017 rParser.PushContext(xCntxt);
3020 void CellSaveStruct::AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts )
3022 m_pCurrCnts = pNewCnts.get();
3024 if (m_xCnts)
3025 m_xCnts->Add( std::move(pNewCnts) );
3026 else
3027 m_xCnts = std::move(pNewCnts);
3030 void CellSaveStruct::InsertCell( SwHTMLParser& rParser,
3031 HTMLTable *pCurTable )
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...
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.
3047 if( rParser.m_nContextStAttrMin == GetContextStAttrMin() )
3049 HTMLAttr** pTable = reinterpret_cast<HTMLAttr**>(rParser.m_xAttrTab.get());
3051 for( auto nCnt = sizeof( HTMLAttrTable ) / sizeof( HTMLAttr* );
3052 nCnt--; ++pTable )
3054 OSL_ENSURE( !*pTable, "The attribute table isn't empty" );
3057 #endif
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 );
3070 void CellSaveStruct::StartNoBreak( const SwPosition& rPos )
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()) )
3078 m_bNoBreak = true;
3082 void CellSaveStruct::EndNoBreak( const SwPosition& rPos )
3084 if( m_bNoBreak )
3086 m_pNoBreakEndNodeIndex.reset( new SwNodeIndex( rPos.nNode ) );
3087 m_nNoBreakEndContentPos = rPos.nContent.GetIndex();
3088 m_bNoBreak = false;
3092 void CellSaveStruct::CheckNoBreak( const SwPosition& rPos )
3094 if (!(m_xCnts && m_pCurrCnts == m_xCnts.get()))
3095 return;
3097 if( m_bNoBreak )
3099 // <NOBR> wasn't closed
3100 m_xCnts->SetNoBreak();
3102 else if( m_pNoBreakEndNodeIndex &&
3103 m_pNoBreakEndNodeIndex->GetIndex() == rPos.nNode.GetIndex() )
3105 if( m_nNoBreakEndContentPos == rPos.nContent.GetIndex() )
3107 // <NOBR> was closed immediately before the cell end
3108 m_xCnts->SetNoBreak();
3110 else if( m_nNoBreakEndContentPos + 1 == rPos.nContent.GetIndex() )
3112 SwTextNode const*const pTextNd(rPos.nNode.GetNode().GetTextNode());
3113 if( pTextNd )
3115 sal_Unicode const cLast =
3116 pTextNd->GetText()[m_nNoBreakEndContentPos];
3117 if( ' '==cLast || '\x0a'==cLast )
3119 // There's just a blank or a newline between the <NOBR> and the cell end
3120 m_xCnts->SetNoBreak();
3127 std::unique_ptr<HTMLTableCnts> SwHTMLParser::InsertTableContents(
3128 bool bHead )
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) );
3135 if( GetNumInfo().GetNumRule() )
3137 // Set the first paragraph to non-enumerated
3138 sal_uInt8 nLvl = GetNumInfo().GetLevel();
3140 SetNodeNum( nLvl );
3143 // Reset attributation start
3144 const SwNodeIndex& rSttPara = m_pPam->GetPoint()->nNode;
3145 sal_Int32 nSttCnt = m_pPam->GetPoint()->nContent.GetIndex();
3147 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
3148 for (sal_uInt16 nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
3150 HTMLAttr *pAttr = *pHTMLAttributes;
3151 while( pAttr )
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;
3159 pAttr = pAttr->GetNext();
3163 return std::make_unique<HTMLTableCnts>( pStNd );
3166 sal_uInt16 SwHTMLParser::IncGrfsThatResizeTable()
3168 return m_xTable ? m_xTable->IncGrfsThatResize() : 0;
3171 void SwHTMLParser::RegisterDrawObjectToTable( HTMLTable *pCurTable,
3172 SdrObject *pObj, sal_uInt8 nPercentWidth )
3174 pCurTable->RegisterDrawObject( pObj, nPercentWidth );
3177 void SwHTMLParser::BuildTableCell( HTMLTable *pCurTable, bool bReadOptions,
3178 bool bHead )
3180 if( !IsParserWorking() && m_vPendingStack.empty() )
3181 return;
3183 ::comphelper::FlagRestorationGuard g(m_isInTableStructure, false);
3184 std::unique_ptr<CellSaveStruct> xSaveStruct;
3186 HtmlTokenId nToken = HtmlTokenId::NONE;
3187 bool bPending = false;
3188 if( !m_vPendingStack.empty() )
3190 xSaveStruct.reset(static_cast<CellSaveStruct*>(m_vPendingStack.back().pData.release()));
3192 m_vPendingStack.pop_back();
3193 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
3194 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
3196 SaveState( nToken );
3198 else
3200 // <TH> resp. <TD> were already read
3201 if (m_xTable->IsOverflowing())
3203 SaveState( HtmlTokenId::NONE );
3204 return;
3207 if( !pCurTable->GetContext() )
3209 bool bTopTable = m_xTable.get() == pCurTable;
3211 // the table has no content yet, this means the actual table needs
3212 // to be created first
3214 static sal_uInt16 aWhichIds[] =
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,
3226 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), aWhichIds );
3227 SvxCSS1PropertyInfo aPropInfo;
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 )
3237 if( SfxItemState::SET == aItemSet.GetItemState(
3238 RES_BACKGROUND, false, &pItem ) )
3240 pCurTable->SetBGBrush( *static_cast<const SvxBrushItem *>(pItem) );
3241 aItemSet.ClearItem( RES_BACKGROUND );
3243 if( SfxItemState::SET == aItemSet.GetItemState(
3244 RES_PARATR_SPLIT, false, &pItem ) )
3246 aItemSet.Put(
3247 SwFormatLayoutSplit( static_cast<const SvxFormatSplitItem *>(pItem)
3248 ->GetValue() ) );
3249 aItemSet.ClearItem( RES_PARATR_SPLIT );
3253 sal_uInt16 nLeftSpace = 0;
3254 sal_uInt16 nRightSpace = 0;
3255 short nIndent;
3256 GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent );
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 )
3265 SvxAdjust eTableAdjust = m_xTable->GetTableAdjust(false);
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();
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!" );
3279 bool bAppend = false;
3280 if( bForceFrame )
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);
3286 else
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();
3295 if( bAppend )
3297 if( !m_pPam->GetPoint()->nContent.GetIndex() )
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 );
3304 HTMLAttr* pTmp =
3305 new HTMLAttr( *m_pPam->GetPoint(), aFontHeight, nullptr, std::shared_ptr<HTMLAttrTable>() );
3306 m_aSetAttrTab.push_back( pTmp );
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 );
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 );
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
3324 AppendTextNode( AM_NOSPACE );
3325 bAppended = true;
3327 else if( !m_aParaAttrs.empty() )
3329 if( !bForceFrame )
3331 // The paragraph will be moved right behind the table.
3332 // That's why we remove all hard attributes of that paragraph
3334 for(HTMLAttr* i : m_aParaAttrs)
3335 i->Invalidate();
3338 m_aParaAttrs.clear();
3341 pSavePos = new SwPosition( *m_pPam->GetPoint() );
3343 else if( pCurTable->HasParentSection() )
3345 bParentLFStripped = StripTrailingLF() > 0;
3347 // Close paragraph resp. headers
3348 m_nOpenParaToken = HtmlTokenId::NONE;
3349 m_nFontStHeadStart = m_nFontStMin;
3351 // The hard attributes on that paragraph are never gonna be invalid anymore
3352 m_aParaAttrs.clear();
3355 // create a table context
3356 std::unique_ptr<HTMLTableContext> pTCntxt(
3357 new HTMLTableContext( pSavePos, m_nContextStMin,
3358 m_nContextStAttrMin ) );
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()) )
3364 SplitAttrTab(pTCntxt->m_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 );
3376 else
3378 SaveAttrTab(pTCntxt->m_xAttrTab);
3379 if( bTopTable && !bAppended )
3381 pPostIts.emplace();
3382 SetAttr( true, true, &*pPostIts );
3385 m_bNoParSpace = false;
3387 // Save current numbering and turn it off
3388 pTCntxt->SetNumInfo( GetNumInfo() );
3389 GetNumInfo().Clear();
3390 pTCntxt->SavePREListingXMP( *this );
3392 if( bTopTable )
3394 if( bForceFrame )
3396 // the table should be put in a text frame
3398 SfxItemSet aFrameSet( m_xDoc->GetAttrPool(),
3399 svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} );
3400 if( !pCurTable->IsNewDoc() )
3401 Reader::ResetFrameFormatAttrs( aFrameSet );
3403 css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
3404 sal_Int16 eHori;
3406 switch( pCurTable->GetTableAdjust(true) )
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;
3422 SetAnchorAndAdjustment( text::VertOrientation::NONE, eHori, aFrameSet,
3423 true );
3424 aFrameSet.Put( SwFormatSurround(eSurround) );
3426 SwFormatFrameSize aFrameSize( SwFrameSize::Variable, 20*MM50, MINLAY );
3427 aFrameSize.SetWidthPercent( 100 );
3428 aFrameSet.Put( aFrameSize );
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) );
3437 RndStdIds eAnchorId = aFrameSet.
3438 Get( RES_ANCHOR ).
3439 GetAnchorId();
3440 SwFrameFormat *pFrameFormat = m_xDoc->MakeFlySection(
3441 eAnchorId, m_pPam->GetPoint(), &aFrameSet );
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 );
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;
3462 if( bForceFrame )
3464 SwNodeIndex aDstIdx( m_pPam->GetPoint()->nNode );
3465 m_pPam->Move( fnMoveBackward );
3466 m_xDoc->GetNodes().Delete( aDstIdx );
3468 else
3470 if (bStyleParsed && pFrameFormat)
3472 m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo );
3473 pFrameFormat->SetFormatAttr( aItemSet );
3475 m_pPam->Move( fnMoveBackward );
3478 SwNode const*const pNd = & m_pPam->GetPoint()->nNode.GetNode();
3479 SwTextNode *const pOldTextNd = (!bAppended && !bForceFrame) ?
3480 pSavePos->nNode.GetNode().GetTextNode() : nullptr;
3482 if (pFrameFormat && pOldTextNd)
3484 const SfxPoolItem* pItem2;
3485 if( SfxItemState::SET == pOldTextNd->GetSwAttrSet()
3486 .GetItemState( RES_PAGEDESC, false, &pItem2 ) &&
3487 static_cast<const SwFormatPageDesc *>(pItem2)->GetPageDesc() )
3489 pFrameFormat->SetFormatAttr( *pItem2 );
3490 pOldTextNd->ResetAttr( RES_PAGEDESC );
3492 if( SfxItemState::SET == pOldTextNd->GetSwAttrSet()
3493 .GetItemState( RES_BREAK, true, &pItem2 ) )
3495 switch( static_cast<const SvxFormatBreakItem *>(pItem2)->GetBreak() )
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;
3509 if( !bAppended && pPostIts )
3511 // set still-existing PostIts to the first paragraph of the table
3512 InsertAttrs( std::move(*pPostIts) );
3513 pPostIts.reset();
3516 pTCntxt->SetTableNode( const_cast<SwTableNode *>(pNd->FindTableNode()) );
3518 auto pTableNode = pTCntxt->GetTableNode();
3519 pCurTable->SetTable( pTableNode, std::move(pTCntxt),
3520 nLeftSpace, nRightSpace,
3521 pSwTable, bForceFrame );
3523 OSL_ENSURE( !pPostIts, "unused PostIts" );
3525 else
3527 // still open sections need to be deleted
3528 if( EndSections( bParentLFStripped ) )
3529 bParentLFStripped = false;
3531 if( pCurTable->HasParentSection() )
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();
3538 if( pPostIts )
3540 // move still existing PostIts to the end of the current paragraph
3541 InsertAttrs( std::move(*pPostIts) );
3542 pPostIts.reset();
3546 SwNode const*const pNd = & m_pPam->GetPoint()->nNode.GetNode();
3547 const SwStartNode *pStNd = (m_xTable->m_bFirstCell ? pNd->FindTableNode()
3548 : pNd->FindTableBoxStartNode() );
3550 pCurTable->SetTable( pStNd, std::move(pTCntxt), nLeftSpace, nRightSpace );
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;
3560 xSaveStruct.reset(new CellSaveStruct(*this, pCurTable, bHead, bReadOptions));
3562 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
3563 SaveState( HtmlTokenId::NONE );
3566 if( nToken == HtmlTokenId::NONE )
3567 nToken = GetNextToken(); // Token after <TABLE>
3569 bool bDone = false;
3570 while( (IsParserWorking() && !bDone) || bPending )
3572 SaveState( nToken );
3574 nToken = FilterToken( nToken );
3576 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || xSaveStruct->IsInSection(),
3577 "Where is the section??" );
3578 if( m_vPendingStack.empty() && m_bCallNextToken && xSaveStruct->IsInSection() )
3580 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
3581 NextToken( nToken );
3583 else switch( nToken )
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:
3604 bool bHasToFly = false;
3605 SvxAdjust eTabAdjust = SvxAdjust::End;
3606 if( m_vPendingStack.empty() )
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;
3612 // HACK: create a section for a table that goes in a text frame
3613 if( !xSaveStruct->IsInSection() )
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)
3621 if( HtmlOptionId::ALIGN==rOption.GetToken() )
3623 SvxAdjust eAdjust = rOption.GetEnum( aHTMLPAlignTable, SvxAdjust::End );
3624 bNeedsSection = SvxAdjust::Left == eAdjust ||
3625 SvxAdjust::Right == eAdjust;
3626 break;
3629 if( bNeedsSection )
3631 xSaveStruct->AddContents(
3632 InsertTableContents(bHead ) );
3635 else
3637 // If Flys are anchored in the current paragraph,
3638 // the table needs to get in a text frame
3639 bHasToFly = HasCurrentParaFlys(false,true);
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;
3649 std::shared_ptr<HTMLTable> xSubTable = BuildTable(eTabAdjust,
3650 bHead,
3651 xSaveStruct->IsInSection(),
3652 bHasToFly);
3653 if( SvParserState::Pending != GetStatus() )
3655 // Only if the table is really complete
3656 if (xSubTable)
3658 OSL_ENSURE( xSubTable->GetTableAdjust(false)!= SvxAdjust::Left &&
3659 xSubTable->GetTableAdjust(false)!= SvxAdjust::Right,
3660 "left or right aligned tables belong in frames" );
3662 auto& rParentContents = xSubTable->GetParentContents();
3663 if (rParentContents)
3665 OSL_ENSURE( !xSaveStruct->IsInSection(),
3666 "Where is the section" );
3668 // If there's no table coming, we have a section
3669 xSaveStruct->AddContents(std::move(rParentContents));
3672 const SwStartNode *pCapStNd =
3673 xSubTable->GetCaptionStartNode();
3675 if (xSubTable->GetContext())
3677 OSL_ENSURE( !xSubTable->GetContext()->GetFrameFormat(),
3678 "table in frame" );
3680 if( pCapStNd && xSubTable->IsTopCaption() )
3682 xSaveStruct->AddContents(
3683 std::make_unique<HTMLTableCnts>(pCapStNd) );
3686 xSaveStruct->AddContents(
3687 std::make_unique<HTMLTableCnts>(xSubTable) );
3689 if( pCapStNd && !xSubTable->IsTopCaption() )
3691 xSaveStruct->AddContents(
3692 std::make_unique<HTMLTableCnts>(pCapStNd) );
3695 // We don't have a section anymore
3696 xSaveStruct->ClearIsInSection();
3698 else if( pCapStNd )
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) );
3705 // We don't have a section anymore
3706 xSaveStruct->ClearIsInSection();
3710 m_xTable = xSaveStruct->m_xTable;
3713 break;
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;
3720 case HtmlTokenId::NOBR_OFF:
3721 xSaveStruct->EndNoBreak( *m_pPam->GetPoint() );
3722 break;
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;
3730 case HtmlTokenId::MARQUEE_ON:
3731 if( !xSaveStruct->IsInSection() )
3733 // create a new section, the PaM is gonna be there
3734 xSaveStruct->AddContents(
3735 InsertTableContents( bHead ) );
3737 m_bCallNextToken = true;
3738 NewMarquee( pCurTable );
3739 break;
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() )
3750 // add a new section, the PaM's gonna be there
3751 xSaveStruct->AddContents(
3752 InsertTableContents( bHead ) );
3755 if( IsParserWorking() || bPending )
3756 NextToken( nToken );
3757 break;
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 );
3766 if( !bDone )
3767 nToken = GetNextToken();
3770 if( SvParserState::Pending == GetStatus() )
3772 m_vPendingStack.emplace_back( bHead ? HtmlTokenId::TABLEHEADER_ON
3773 : HtmlTokenId::TABLEDATA_ON );
3774 m_vPendingStack.back().pData = std::move(xSaveStruct);
3776 return;
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()) )
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 ));
3794 if (!pStNd)
3795 eState = SvParserState::Error;
3796 else
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
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 );
3814 xSaveStruct->AddContents( std::make_unique<HTMLTableCnts>(pStNd) );
3815 xSaveStruct->ClearIsInSection();
3818 if( xSaveStruct->IsInSection() )
3820 xSaveStruct->CheckNoBreak( *m_pPam->GetPoint() );
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 )
3826 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3827 EndContext(xCntxt.get());
3830 // Remove LFs at the paragraph end
3831 if (StripTrailingLF() == 0 && !m_pPam->GetPoint()->nContent.GetIndex())
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();
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());
3845 else
3847 // Close all still open contexts
3848 while( m_aContexts.size() > m_nContextStAttrMin )
3850 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3851 if (!xCntxt)
3852 break;
3853 ClearContext(xCntxt.get());
3857 // end an enumeration
3858 GetNumInfo().Clear();
3860 SetAttr( false );
3862 xSaveStruct->InsertCell( *this, pCurTable );
3864 // we're probably before a <TH>, <TD>, <TR> or </TABLE>
3865 xSaveStruct.reset();
3868 namespace {
3870 class RowSaveStruct : public SwPendingData
3872 public:
3873 SvxAdjust eAdjust;
3874 sal_Int16 eVertOri;
3875 bool bHasCells;
3877 RowSaveStruct() :
3878 eAdjust( SvxAdjust::End ), eVertOri( text::VertOrientation::TOP ), bHasCells( false )
3884 void SwHTMLParser::BuildTableRow( HTMLTable *pCurTable, bool bReadOptions,
3885 SvxAdjust eGrpAdjust,
3886 sal_Int16 eGrpVertOri )
3888 // <TR> was already read
3890 if( !IsParserWorking() && m_vPendingStack.empty() )
3891 return;
3893 HtmlTokenId nToken = HtmlTokenId::NONE;
3894 std::unique_ptr<RowSaveStruct> xSaveStruct;
3896 bool bPending = false;
3897 if( !m_vPendingStack.empty() )
3899 xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
3901 m_vPendingStack.pop_back();
3902 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
3903 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
3905 SaveState( nToken );
3907 else
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);
3916 if( bReadOptions )
3918 const HTMLOptions& rHTMLOptions = GetOptions();
3919 for (size_t i = rHTMLOptions.size(); i; )
3921 const HTMLOption& rOption = rHTMLOptions[--i];
3922 switch( rOption.GetToken() )
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() )
3938 rOption.GetColor( aBGColor );
3939 bBGColor = true;
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;
3956 if( !aId.isEmpty() )
3957 InsertBookmark( aId );
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 );
3967 if( nToken == HtmlTokenId::NONE )
3968 nToken = GetNextToken();
3970 bool bDone = false;
3971 while( (IsParserWorking() && !bDone) || bPending )
3973 SaveState( nToken );
3975 nToken = FilterToken( nToken );
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()) )
3983 /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
3984 NextToken( nToken );
3986 else switch( nToken )
3988 case HtmlTokenId::TABLE_ON:
3989 if( !pCurTable->GetContext() )
3991 SkipToken();
3992 bDone = true;
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() )
4014 xSaveStruct->bHasCells = true;
4015 bDone = m_xTable->IsOverflowing();
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;
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 );
4066 if( !bDone )
4067 nToken = GetNextToken();
4070 if( SvParserState::Pending == GetStatus() )
4072 m_vPendingStack.emplace_back( HtmlTokenId::TABLEROW_ON );
4073 m_vPendingStack.back().pData = std::move(xSaveStruct);
4075 else
4077 pCurTable->CloseRow(!xSaveStruct->bHasCells);
4078 xSaveStruct.reset();
4081 // we're probably before <TR> or </TABLE>
4084 void SwHTMLParser::BuildTableSection( HTMLTable *pCurTable,
4085 bool bReadOptions,
4086 bool bHead )
4088 // <THEAD>, <TBODY> resp. <TFOOT> were read already
4089 if( !IsParserWorking() && m_vPendingStack.empty() )
4090 return;
4092 HtmlTokenId nToken = HtmlTokenId::NONE;
4093 bool bPending = false;
4094 std::unique_ptr<RowSaveStruct> xSaveStruct;
4096 if( !m_vPendingStack.empty() )
4098 xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
4100 m_vPendingStack.pop_back();
4101 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4102 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4104 SaveState( nToken );
4106 else
4108 xSaveStruct.reset(new RowSaveStruct);
4110 if( bReadOptions )
4112 const HTMLOptions& rHTMLOptions = GetOptions();
4113 for (size_t i = rHTMLOptions.size(); i; )
4115 const HTMLOption& rOption = rHTMLOptions[--i];
4116 switch( rOption.GetToken() )
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;
4135 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4136 SaveState( HtmlTokenId::NONE );
4139 if( nToken == HtmlTokenId::NONE )
4140 nToken = GetNextToken();
4142 bool bDone = false;
4143 while( (IsParserWorking() && !bDone) || bPending )
4145 SaveState( nToken );
4147 nToken = FilterToken( nToken );
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()) )
4155 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
4156 NextToken( nToken );
4158 else switch( nToken )
4160 case HtmlTokenId::TABLE_ON:
4161 if( !pCurTable->GetContext() )
4163 SkipToken();
4164 bDone = true;
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 );
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 );
4224 if( !bDone )
4225 nToken = GetNextToken();
4228 if( SvParserState::Pending == GetStatus() )
4230 m_vPendingStack.emplace_back( bHead ? HtmlTokenId::THEAD_ON
4231 : HtmlTokenId::TBODY_ON );
4232 m_vPendingStack.back().pData = std::move(xSaveStruct);
4234 else
4236 pCurTable->CloseSection( bHead );
4237 xSaveStruct.reset();
4240 // now we stand (perhaps) in front of <TBODY>,... or </TABLE>
4243 namespace {
4245 struct TableColGrpSaveStruct : public SwPendingData
4247 sal_uInt16 nColGrpSpan;
4248 sal_uInt16 nColGrpWidth;
4249 bool bRelColGrpWidth;
4250 SvxAdjust eColGrpAdjust;
4251 sal_Int16 eColGrpVertOri;
4253 inline TableColGrpSaveStruct();
4255 inline void CloseColGroup( HTMLTable *pTable );
4260 inline TableColGrpSaveStruct::TableColGrpSaveStruct() :
4261 nColGrpSpan( 1 ), nColGrpWidth( 0 ),
4262 bRelColGrpWidth( false ), eColGrpAdjust( SvxAdjust::End ),
4263 eColGrpVertOri( text::VertOrientation::TOP )
4266 inline void TableColGrpSaveStruct::CloseColGroup( HTMLTable *pTable )
4268 pTable->CloseColGroup( nColGrpSpan, nColGrpWidth,
4269 bRelColGrpWidth, eColGrpAdjust, eColGrpVertOri );
4272 void SwHTMLParser::BuildTableColGroup( HTMLTable *pCurTable,
4273 bool bReadOptions )
4275 // <COLGROUP> was read already if bReadOptions is set
4277 if( !IsParserWorking() && m_vPendingStack.empty() )
4278 return;
4280 HtmlTokenId nToken = HtmlTokenId::NONE;
4281 bool bPending = false;
4282 std::unique_ptr<TableColGrpSaveStruct> pSaveStruct;
4284 if( !m_vPendingStack.empty() )
4286 pSaveStruct.reset(static_cast<TableColGrpSaveStruct*>(m_vPendingStack.back().pData.release()));
4289 m_vPendingStack.pop_back();
4290 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4291 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4293 SaveState( nToken );
4295 else
4298 pSaveStruct.reset(new TableColGrpSaveStruct);
4299 if( bReadOptions )
4301 const HTMLOptions& rColGrpOptions = GetOptions();
4302 for (size_t i = rColGrpOptions.size(); i; )
4304 const HTMLOption& rOption = rColGrpOptions[--i];
4305 switch( rOption.GetToken() )
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)
4314 SAL_INFO("sw.html", "ignoring huge SPAN " << pSaveStruct->nColGrpSpan);
4315 pSaveStruct->nColGrpSpan = 1;
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;
4336 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4337 SaveState( HtmlTokenId::NONE );
4340 if( nToken == HtmlTokenId::NONE )
4341 nToken = GetNextToken(); // naechstes Token
4343 bool bDone = false;
4344 while( (IsParserWorking() && !bDone) || bPending )
4346 SaveState( nToken );
4348 nToken = FilterToken( nToken );
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()) )
4356 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
4357 NextToken( nToken );
4359 else switch( nToken )
4361 case HtmlTokenId::TABLE_ON:
4362 if( !pCurTable->GetContext() )
4364 SkipToken();
4365 bDone = true;
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:
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;
4388 const HTMLOptions& rColOptions = GetOptions();
4389 for (size_t i = rColOptions.size(); i; )
4391 const HTMLOption& rOption = rColOptions[--i];
4392 switch( rOption.GetToken() )
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)
4401 SAL_INFO("sw.html", "ignoring huge SPAN " << nColSpan);
4402 nColSpan = 1;
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;
4420 pCurTable->InsertCol( nColSpan, nColWidth, bRelColWidth,
4421 eColAdjust, eColVertOri );
4423 // the attributes in <COLGRP> should be ignored, if there are <COL> elements
4424 pSaveStruct->nColGrpSpan = 0;
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 );
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 );
4449 if( !bDone )
4450 nToken = GetNextToken();
4453 if( SvParserState::Pending == GetStatus() )
4455 m_vPendingStack.emplace_back( HtmlTokenId::COL_ON );
4456 m_vPendingStack.back().pData = std::move(pSaveStruct);
4458 else
4460 pSaveStruct->CloseColGroup( pCurTable );
4464 class CaptionSaveStruct : public SectionSaveStruct
4466 SwPosition m_aSavePos;
4467 SwHTMLNumRuleInfo m_aNumRuleInfo; // valid numbering
4469 public:
4471 std::shared_ptr<HTMLAttrTable> m_xAttrTab; // attributes
4473 CaptionSaveStruct( SwHTMLParser& rParser, const SwPosition& rPos ) :
4474 SectionSaveStruct( rParser ), m_aSavePos( rPos ),
4475 m_xAttrTab(std::make_shared<HTMLAttrTable>())
4477 rParser.SaveAttrTab(m_xAttrTab);
4479 // The current numbering was remembered and just needs to be closed
4480 m_aNumRuleInfo.Set( rParser.GetNumInfo() );
4481 rParser.GetNumInfo().Clear();
4484 const SwPosition& GetPos() const { return m_aSavePos; }
4486 void RestoreAll( SwHTMLParser& rParser )
4488 // Recover the old stack
4489 Restore( rParser );
4491 // Recover the old attribute tables
4492 rParser.RestoreAttrTab(m_xAttrTab);
4494 // Re-open the old numbering
4495 rParser.GetNumInfo().Set( m_aNumRuleInfo );
4499 void SwHTMLParser::BuildTableCaption( HTMLTable *pCurTable )
4501 // <CAPTION> was read already
4503 if( !IsParserWorking() && m_vPendingStack.empty() )
4504 return;
4506 HtmlTokenId nToken = HtmlTokenId::NONE;
4507 std::unique_ptr<CaptionSaveStruct> xSaveStruct;
4509 if( !m_vPendingStack.empty() )
4511 xSaveStruct.reset(static_cast<CaptionSaveStruct*>(m_vPendingStack.back().pData.release()));
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?" );
4517 SaveState( nToken );
4519 else
4521 if (m_xTable->IsOverflowing())
4523 SaveState( HtmlTokenId::NONE );
4524 return;
4527 bool bTop = true;
4528 const HTMLOptions& rHTMLOptions = GetOptions();
4529 for ( size_t i = rHTMLOptions.size(); i; )
4531 const HTMLOption& rOption = rHTMLOptions[--i];
4532 if( HtmlOptionId::ALIGN == rOption.GetToken() )
4534 if (rOption.GetString().equalsIgnoreAsciiCase(
4535 OOO_STRING_SVTOOLS_HTML_VA_bottom))
4537 bTop = false;
4542 // Remember old PaM position
4543 xSaveStruct.reset(new CaptionSaveStruct(*this, *m_pPam->GetPoint()));
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 );
4553 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::CAPTION_ON));
4555 // Table headers are always centered
4556 NewAttr(m_xAttrTab, &m_xAttrTab->pAdjust, SvxAdjustItem(SvxAdjust::Center, RES_PARATR_ADJUST));
4558 HTMLAttrs &rAttrs = xCntxt->GetAttrs();
4559 rAttrs.push_back( m_xAttrTab->pAdjust );
4561 PushContext(xCntxt);
4563 // Remember the start node of the section at the table
4564 pCurTable->SetCaption( pStNd, bTop );
4566 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4567 SaveState( HtmlTokenId::NONE );
4570 if( nToken == HtmlTokenId::NONE )
4571 nToken = GetNextToken();
4573 // </CAPTION> is needed according to DTD
4574 bool bDone = false;
4575 while( IsParserWorking() && !bDone )
4577 SaveState( nToken );
4579 nToken = FilterToken( nToken );
4581 switch( nToken )
4583 case HtmlTokenId::TABLE_ON:
4584 if( m_vPendingStack.empty() )
4586 xSaveStruct->m_xTable = m_xTable;
4587 bool bHasToFly = xSaveStruct->m_xTable.get() != pCurTable;
4588 BuildTable( pCurTable->GetTableAdjust( true ),
4589 false, true, bHasToFly );
4591 else
4593 BuildTable( SvxAdjust::End );
4595 if( SvParserState::Pending != GetStatus() )
4597 m_xTable = xSaveStruct->m_xTable;
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;
4610 case HtmlTokenId::CAPTION_OFF:
4611 bDone = true;
4612 break;
4613 default:
4614 if( !m_vPendingStack.empty() )
4616 m_vPendingStack.pop_back();
4617 OSL_ENSURE( m_vPendingStack.empty(), "Further it can't go!" );
4620 if( IsParserWorking() )
4621 NextToken( nToken );
4622 break;
4625 if( IsParserWorking() )
4626 SaveState( HtmlTokenId::NONE );
4628 if( !bDone )
4629 nToken = GetNextToken();
4632 if( SvParserState::Pending==GetStatus() )
4634 m_vPendingStack.emplace_back( HtmlTokenId::CAPTION_ON );
4635 m_vPendingStack.back().pData = std::move(xSaveStruct);
4636 return;
4639 // end all still open contexts
4640 while( m_aContexts.size() > m_nContextStAttrMin+1 )
4642 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
4643 EndContext(xCntxt.get());
4646 bool bLFStripped = StripTrailingLF() > 0;
4648 if (m_xTable.get() == pCurTable)
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 );
4655 else
4657 // Strip LFs at the end of the paragraph
4658 if( !m_pPam->GetPoint()->nContent.GetIndex() && !bLFStripped )
4659 StripTrailingPara();
4662 // If there's an adjustment for the cell, we need to close it
4663 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
4664 if (xCntxt)
4666 EndContext(xCntxt.get());
4667 xCntxt.reset();
4670 SetAttr( false );
4672 // Recover stack and attribute table
4673 xSaveStruct->RestoreAll(*this);
4675 // Recover PaM
4676 *m_pPam->GetPoint() = xSaveStruct->GetPos();
4679 namespace {
4681 class TableSaveStruct : public SwPendingData
4683 public:
4684 std::shared_ptr<HTMLTable> m_xCurrentTable;
4686 explicit TableSaveStruct(const std::shared_ptr<HTMLTable>& rCurTable)
4687 : m_xCurrentTable(rCurTable)
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 );
4698 void TableSaveStruct::MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc )
4700 m_xCurrentTable->MakeTable(nullptr, nWidth);
4702 HTMLTableContext *pTCntxt = m_xCurrentTable->GetContext();
4703 OSL_ENSURE( pTCntxt, "Where is the table context" );
4705 SwTableNode *pTableNd = pTCntxt->GetTableNode();
4706 OSL_ENSURE( pTableNd, "Where is the table node" );
4708 if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pTableNd )
4710 // If there's already a layout, the BoxFrames need to be regenerated at this table
4712 if( pTCntxt->GetFrameFormat() )
4714 pTCntxt->GetFrameFormat()->DelFrames();
4715 pTableNd->DelFrames();
4716 pTCntxt->GetFrameFormat()->MakeFrames();
4718 else
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);
4728 rPos = *pTCntxt->GetPos();
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 )
4745 bool bBorderColor = false;
4746 bool bHasFrame = false, bHasRules = false;
4748 for (size_t i = rOptions.size(); i; )
4750 const HTMLOption& rOption = rOptions[--i];
4751 switch( rOption.GetToken() )
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:
4778 if( rOption.GetEnum( eAdjust, aHTMLPAlignTable ) )
4780 bTableAdjust = true;
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))
4793 nBorder = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4795 else
4796 nBorder = 1;
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() )
4816 rOption.GetColor( aBGColor );
4817 bBGColor = true;
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;
4850 if( nCols && !nWidth )
4852 nWidth = 100;
4853 bPercentWidth = true;
4856 // If BORDER=0 or no BORDER given, then there shouldn't be a border
4857 if( 0==nBorder || USHRT_MAX==nBorder )
4859 eFrame = HTMLTableFrame::Void;
4860 eRules = HTMLTableRules::NONE;
4864 namespace
4866 class IndexInRange
4868 private:
4869 SwNodeIndex maStart;
4870 SwNodeIndex maEnd;
4871 public:
4872 explicit IndexInRange(const SwNodeIndex& rStart, const SwNodeIndex& rEnd)
4873 : maStart(rStart)
4874 , maEnd(rEnd)
4877 bool operator()(const SwHTMLTextFootnote& rTextFootnote) const
4879 const SwNodeIndex aTextIdx(rTextFootnote.pTextFootnote->GetTextNode());
4880 return aTextIdx >= maStart && aTextIdx <= maEnd;
4885 void SwHTMLParser::ClearFootnotesMarksInRange(const SwNodeIndex& rMkNdIdx, const SwNodeIndex& rPtNdIdx)
4887 //similarly for footnotes
4888 if (m_pFootEndNoteImpl)
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())
4894 m_pFootEndNoteImpl.reset();
4898 //follow DelFlyInRange pattern here
4899 assert(rMkNdIdx.GetIndex() <= rPtNdIdx.GetIndex());
4901 SwDoc& rDoc = rMkNdIdx.GetNode().GetDoc();
4903 //ofz#9733 drop bookmarks in this range
4904 IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
4905 pMarkAccess->deleteMarks(rMkNdIdx, SwNodeIndex(rPtNdIdx, 1), nullptr, nullptr, nullptr);
4907 SwFrameFormats& rTable = *rDoc.GetSpzFrameFormats();
4908 for ( auto i = rTable.size(); i; )
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 ))
4918 if( rPtNdIdx != pAPos->nNode )
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)
4925 ClearFootnotesMarksInRange(*rContent.GetContentIdx(),
4926 SwNodeIndex(*rContent.GetContentIdx()->GetNode().EndOfSectionNode()));
4933 void SwHTMLParser::DeleteSection(SwStartNode* pSttNd)
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);
4940 //similarly for footnotes
4941 SwNodeIndex aSttIdx(*pSttNd), aEndIdx(*pSttNd->EndOfSectionNode());
4942 ClearFootnotesMarksInRange(aSttIdx, aEndIdx);
4944 m_xDoc->getIDocumentContentOperations().DeleteSection(pSttNd);
4946 if (pObjectFormat)
4948 if (aWatch.WasDeleted())
4949 m_pMarquee = nullptr;
4950 else
4951 aWatch.EndListeningAll();
4955 std::shared_ptr<HTMLTable> SwHTMLParser::BuildTable(SvxAdjust eParentAdjust,
4956 bool bIsParentHead,
4957 bool bHasParentSection,
4958 bool bHasToFly)
4960 TableDepthGuard aGuard(*this);
4961 if (aGuard.TooDeep())
4962 eState = SvParserState::Error;
4964 if (!IsParserWorking() && m_vPendingStack.empty())
4965 return std::shared_ptr<HTMLTable>();
4967 ::comphelper::FlagRestorationGuard g(m_isInTableStructure, true);
4968 HtmlTokenId nToken = HtmlTokenId::NONE;
4969 bool bPending = false;
4970 std::unique_ptr<TableSaveStruct> xSaveStruct;
4972 if( !m_vPendingStack.empty() )
4974 xSaveStruct.reset(static_cast<TableSaveStruct*>(m_vPendingStack.back().pData.release()));
4976 m_vPendingStack.pop_back();
4977 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4978 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4980 SaveState( nToken );
4982 else
4984 m_xTable.reset();
4985 HTMLTableOptions aTableOptions(GetOptions(), eParentAdjust);
4987 if (!aTableOptions.aId.isEmpty())
4988 InsertBookmark(aTableOptions.aId);
4990 std::shared_ptr<HTMLTable> xCurTable(std::make_shared<HTMLTable>(this,
4991 bIsParentHead,
4992 bHasParentSection,
4993 bHasToFly,
4994 aTableOptions));
4995 m_xTable = xCurTable;
4997 xSaveStruct.reset(new TableSaveStruct(xCurTable));
4999 // Is pending on the first GetNextToken, needs to be re-read on each construction
5000 SaveState( HtmlTokenId::NONE );
5003 std::shared_ptr<HTMLTable> xCurTable = xSaveStruct->m_xCurrentTable;
5005 // </TABLE> is needed according to DTD
5006 if( nToken == HtmlTokenId::NONE )
5007 nToken = GetNextToken();
5009 bool bDone = false;
5010 while( (IsParserWorking() && !bDone) || bPending )
5012 SaveState( nToken );
5014 nToken = FilterToken( nToken );
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()) )
5022 /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
5023 NextToken( nToken );
5025 else switch( nToken )
5027 case HtmlTokenId::TABLE_ON:
5028 if( !xCurTable->GetContext() )
5030 // If there's no table added, read the next table'
5031 SkipToken();
5032 bDone = true;
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;
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 );
5091 if( !bDone )
5092 nToken = GetNextToken();
5095 if( SvParserState::Pending == GetStatus() )
5097 m_vPendingStack.emplace_back( HtmlTokenId::TABLE_ON );
5098 m_vPendingStack.back().pData = std::move(xSaveStruct);
5099 return std::shared_ptr<HTMLTable>();
5102 HTMLTableContext *pTCntxt = xCurTable->GetContext();
5103 if( pTCntxt )
5106 // Modify table structure
5107 xCurTable->CloseTable();
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 )
5113 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
5114 if (!xCntxt)
5115 break;
5116 ClearContext(xCntxt.get());
5119 m_nContextStMin = pTCntxt->GetContextStMin();
5120 m_nContextStAttrMin = pTCntxt->GetContextStAttrMin();
5122 if (m_xTable == xCurTable)
5124 // Set table caption
5125 const SwStartNode *pCapStNd = m_xTable->GetCaptionStartNode();
5126 if( pCapStNd )
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
5131 if( pCapStNd->EndOfSectionIndex() - pCapStNd->GetIndex() > 2 )
5133 // Don't copy start node and the last paragraph
5134 SwNodeRange aSrcRg( *pCapStNd, 1,
5135 *pCapStNd->EndOfSectionNode(), -1 );
5137 bool bTop = m_xTable->IsTopCaption();
5138 SwStartNode *pTableStNd = pTCntxt->GetTableNode();
5140 OSL_ENSURE( pTableStNd, "Where is the table node" );
5141 OSL_ENSURE( pTableStNd==m_pPam->GetNode().FindTableNode(),
5142 "Are we in the wrong table?" );
5144 SwNode* pNd;
5145 if( bTop )
5146 pNd = pTableStNd;
5147 else
5148 pNd = pTableStNd->EndOfSectionNode();
5149 SwNodeIndex aDstIdx( *pNd, bTop ? 0 : 1 );
5151 m_xDoc->getIDocumentContentOperations().MoveNodeRange( aSrcRg, aDstIdx,
5152 SwMoveFlags::DEFAULT );
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 )
5160 MovePageDescAttrs( pTableStNd, aSrcRg.aStart.GetIndex(),
5161 false );
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 );
5172 // Process SwTable
5173 sal_uInt16 nBrowseWidth = o3tl::narrowing<sal_uInt16>(GetCurrentBrowseWidth());
5174 xSaveStruct->MakeTable(nBrowseWidth, *m_pPam->GetPoint(), m_xDoc.get());
5177 GetNumInfo().Set( pTCntxt->GetNumInfo() );
5178 pTCntxt->RestorePREListingXMP( *this );
5179 RestoreAttrTab(pTCntxt->m_xAttrTab);
5181 if (m_xTable == xCurTable)
5183 // Set upper paragraph spacing
5184 m_bUpperSpace = true;
5185 SetTextCollAttrs();
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);
5191 // Jump to a table if needed
5192 if( JumpToMarks::Table == m_eJumpTo && m_xTable->GetSwTable() &&
5193 m_xTable->GetSwTable()->GetFrameFormat()->GetName() == m_sJmpMark )
5195 m_bChkJumpMark = true;
5196 m_eJumpTo = JumpToMarks::NONE;
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();
5208 else if (m_xTable == xCurTable)
5210 // There was no table read
5212 // We maybe need to delete a read caption
5213 const SwStartNode *pCapStNd = xCurTable->GetCaptionStartNode();
5214 if( pCapStNd )
5216 m_pPam->SetMark();
5217 m_pPam->DeleteMark();
5218 DeleteSection(const_cast<SwStartNode*>(pCapStNd));
5219 xCurTable->SetCaption( nullptr, false );
5223 if (m_xTable == xCurTable)
5225 xSaveStruct->m_xCurrentTable.reset();
5226 m_xTable.reset();
5229 std::shared_ptr<HTMLTable> xRetTable = xSaveStruct->m_xCurrentTable;
5230 xSaveStruct.reset();
5232 return xRetTable;
5235 bool HTMLTable::PendingDrawObjectsInPaM(SwPaM& rPam) const
5237 if (!m_xResizeDrawObjects)
5238 return false;
5240 bool bRet = false;
5242 sal_uInt16 nCount = m_xResizeDrawObjects->size();
5243 for (sal_uInt16 i = 0; i < nCount && !bRet; ++i)
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())
5252 SwNodeIndex aObjNodeIndex(pPos->nNode);
5253 bRet = (aObjNodeIndex >= rPam.Start()->nNode && aObjNodeIndex <= rPam.End()->nNode);
5257 return bRet;
5260 bool SwHTMLParser::PendingObjectsInPaM(SwPaM& rPam) const
5262 bool bRet = false;
5263 for (const auto& a : m_aTables)
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;
5279 return bRet;
5282 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */