Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / filter / html / htmltab.cxx
blobe1e50865f62484ad1fc4a1669aa58ddf9e5863a9
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 <utility>
24 #include <vcl/svapp.hxx>
25 #include <editeng/boxitem.hxx>
26 #include <editeng/brushitem.hxx>
27 #include <editeng/adjustitem.hxx>
28 #include <editeng/fhgtitem.hxx>
29 #include <editeng/ulspitem.hxx>
30 #include <editeng/lrspitem.hxx>
31 #include <editeng/formatbreakitem.hxx>
32 #include <editeng/spltitem.hxx>
33 #include <unotools/configmgr.hxx>
34 #include <svtools/htmltokn.h>
35 #include <svtools/htmlkywd.hxx>
36 #include <svl/numformat.hxx>
37 #include <svl/urihelper.hxx>
38 #include <svx/sdrobjectuser.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
42 #include <dcontact.hxx>
43 #include <fmtornt.hxx>
44 #include <frmfmt.hxx>
45 #include <fmtfsize.hxx>
46 #include <fmtsrnd.hxx>
47 #include <fmtpdsc.hxx>
48 #include <fmtcntnt.hxx>
49 #include <fmtanchr.hxx>
50 #include <fmtlsplt.hxx>
51 #include <frmatr.hxx>
52 #include <pam.hxx>
53 #include <doc.hxx>
54 #include <IDocumentLayoutAccess.hxx>
55 #include <IDocumentMarkAccess.hxx>
56 #include <ndtxt.hxx>
57 #include <shellio.hxx>
58 #include <poolfmt.hxx>
59 #include <swtable.hxx>
60 #include <cellatr.hxx>
61 #include <htmltbl.hxx>
62 #include <swtblfmt.hxx>
63 #include "htmlnum.hxx"
64 #include "swhtml.hxx"
65 #include "swcss1.hxx"
66 #include <txtftn.hxx>
67 #include <itabenum.hxx>
68 #include <tblafmt.hxx>
69 #include <SwStyleNameMapper.hxx>
70 #include <frameformats.hxx>
72 #define NETSCAPE_DFLT_BORDER 1
73 #define NETSCAPE_DFLT_CELLSPACING 2
75 using ::editeng::SvxBorderLine;
76 using namespace ::com::sun::star;
78 HTMLOptionEnum<sal_Int16> const aHTMLTableVAlignTable[] =
80 { OOO_STRING_SVTOOLS_HTML_VA_top, text::VertOrientation::NONE },
81 { OOO_STRING_SVTOOLS_HTML_VA_middle, text::VertOrientation::CENTER },
82 { OOO_STRING_SVTOOLS_HTML_VA_bottom, text::VertOrientation::BOTTOM },
83 { nullptr, 0 }
86 // table tags options
88 namespace {
90 struct HTMLTableOptions
92 sal_uInt16 nCols;
93 sal_uInt16 nWidth;
94 sal_uInt16 nHeight;
95 sal_uInt16 nCellPadding;
96 sal_uInt16 nCellSpacing;
97 sal_uInt16 nBorder;
98 sal_uInt16 nHSpace;
99 sal_uInt16 nVSpace;
101 SvxAdjust eAdjust;
102 sal_Int16 eVertOri;
103 HTMLTableFrame eFrame;
104 HTMLTableRules eRules;
106 bool bPercentWidth : 1;
107 bool bTableAdjust : 1;
108 bool bBGColor : 1;
110 Color aBorderColor;
111 Color aBGColor;
113 OUString aBGImage, aStyle, aId, aClass, aDir;
115 HTMLTableOptions( const HTMLOptions& rOptions, SvxAdjust eParentAdjust );
118 class HTMLTableContext
120 SwHTMLNumRuleInfo m_aNumRuleInfo; // Numbering valid before the table
122 SwTableNode *m_pTableNd; // table node
123 SwFrameFormat *m_pFrameFormat; // the Fly frame::Frame, containing the table
124 std::unique_ptr<SwPosition> m_pPos; // position behind the table
126 size_t m_nContextStAttrMin;
127 size_t m_nContextStMin;
129 bool m_bRestartPRE : 1;
130 bool m_bRestartXMP : 1;
131 bool m_bRestartListing : 1;
133 HTMLTableContext(const HTMLTableContext&) = delete;
134 HTMLTableContext& operator=(const HTMLTableContext&) = delete;
136 public:
138 std::shared_ptr<HTMLAttrTable> m_xAttrTab; // attributes
140 HTMLTableContext( SwPosition *pPs, size_t nCntxtStMin,
141 size_t nCntxtStAttrMin ) :
142 m_pTableNd( nullptr ),
143 m_pFrameFormat( nullptr ),
144 m_pPos( pPs ),
145 m_nContextStAttrMin( nCntxtStAttrMin ),
146 m_nContextStMin( nCntxtStMin ),
147 m_bRestartPRE( false ),
148 m_bRestartXMP( false ),
149 m_bRestartListing( false ),
150 m_xAttrTab(std::make_shared<HTMLAttrTable>())
152 memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable));
155 void SetNumInfo( const SwHTMLNumRuleInfo& rInf ) { m_aNumRuleInfo.Set(rInf); }
156 const SwHTMLNumRuleInfo& GetNumInfo() const { return m_aNumRuleInfo; };
158 void SavePREListingXMP( SwHTMLParser& rParser );
159 void RestorePREListingXMP( SwHTMLParser& rParser );
161 SwPosition *GetPos() const { return m_pPos.get(); }
163 void SetTableNode( SwTableNode *pNd ) { m_pTableNd = pNd; }
164 SwTableNode *GetTableNode() const { return m_pTableNd; }
166 void SetFrameFormat( SwFrameFormat *pFormat ) { m_pFrameFormat = pFormat; }
167 SwFrameFormat *GetFrameFormat() const { return m_pFrameFormat; }
169 size_t GetContextStMin() const { return m_nContextStMin; }
170 size_t GetContextStAttrMin() const { return m_nContextStAttrMin; }
175 // Cell content is a linked list with SwStartNodes and
176 // HTMLTables.
178 class HTMLTableCnts
180 std::unique_ptr<HTMLTableCnts> m_pNext; // next content
182 // Only one of the next two pointers must be set!
183 const SwStartNode *m_pStartNode; // a paragraph
184 std::shared_ptr<HTMLTable> m_xTable; // a table
186 std::shared_ptr<SwHTMLTableLayoutCnts> m_xLayoutInfo;
188 bool m_bNoBreak;
190 void InitCtor();
192 public:
194 explicit HTMLTableCnts(const SwStartNode* pStNd);
195 explicit HTMLTableCnts(std::shared_ptr<HTMLTable> xTab);
197 ~HTMLTableCnts(); // only allowed in ~HTMLTableCell
199 // Determine SwStartNode and HTMLTable respectively
200 const SwStartNode *GetStartNode() const { return m_pStartNode; }
201 const std::shared_ptr<HTMLTable>& GetTable() const { return m_xTable; }
202 std::shared_ptr<HTMLTable>& GetTable() { return m_xTable; }
204 // Add a new node at the end of the list
205 void Add( std::unique_ptr<HTMLTableCnts> pNewCnts );
207 // Determine next node
208 const HTMLTableCnts *Next() const { return m_pNext.get(); }
209 HTMLTableCnts *Next() { return m_pNext.get(); }
211 inline void SetTableBox( SwTableBox *pBox );
213 void SetNoBreak() { m_bNoBreak = true; }
215 const std::shared_ptr<SwHTMLTableLayoutCnts>& CreateLayoutInfo();
218 namespace {
220 // Cell of a HTML table
221 class HTMLTableCell
223 std::shared_ptr<HTMLTableCnts> m_xContents; // cell content
224 std::shared_ptr<SvxBrushItem> m_xBGBrush; // cell background
225 std::shared_ptr<SvxBoxItem> m_xBoxItem;
227 double m_nValue;
228 sal_uInt32 m_nNumFormat;
229 sal_uInt16 m_nRowSpan; // cell ROWSPAN
230 sal_uInt16 m_nColSpan; // cell COLSPAN
231 sal_uInt16 m_nWidth; // cell WIDTH
232 sal_Int16 m_eVertOrient; // vertical alignment of the cell
233 bool m_bProtected : 1; // cell must not filled
234 bool m_bRelWidth : 1; // nWidth is given in %
235 bool m_bHasNumFormat : 1;
236 bool m_bHasValue : 1;
237 bool m_bNoWrap : 1;
238 bool mbCovered : 1;
240 public:
242 HTMLTableCell(); // new cells always empty
244 // Fill a not empty cell
245 void Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan,
246 sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrush,
247 std::shared_ptr<SvxBoxItem> const& rBoxItem,
248 bool bHasNumFormat, sal_uInt32 nNumFormat,
249 bool bHasValue, double nValue, bool bNoWrap, bool bCovered );
251 // Protect an empty 1x1 cell
252 void SetProtected();
254 // Set/Get cell content
255 void SetContents(std::shared_ptr<HTMLTableCnts> const& rCnts) { m_xContents = rCnts; }
256 const std::shared_ptr<HTMLTableCnts>& GetContents() const { return m_xContents; }
258 // Set/Get cell ROWSPAN/COLSPAN
259 void SetRowSpan( sal_uInt16 nRSpan ) { m_nRowSpan = nRSpan; }
260 sal_uInt16 GetRowSpan() const { return m_nRowSpan; }
262 void SetColSpan( sal_uInt16 nCSpan ) { m_nColSpan = nCSpan; }
263 sal_uInt16 GetColSpan() const { return m_nColSpan; }
265 inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth );
267 const std::shared_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
268 const std::shared_ptr<SvxBoxItem>& GetBoxItem() const { return m_xBoxItem; }
270 inline bool GetNumFormat( sal_uInt32& rNumFormat ) const;
271 inline bool GetValue( double& rValue ) const;
273 sal_Int16 GetVertOri() const { return m_eVertOrient; }
275 // Is the cell filled or protected ?
276 bool IsUsed() const { return m_xContents || m_bProtected; }
278 std::unique_ptr<SwHTMLTableLayoutCell> CreateLayoutInfo();
280 bool IsCovered() const { return mbCovered; }
286 namespace {
288 // Row of a HTML table
289 class HTMLTableRow
291 std::vector<HTMLTableCell> m_aCells; ///< cells of the row
292 std::unique_ptr<SvxBrushItem> m_xBGBrush; // background of cell from STYLE
294 SvxAdjust m_eAdjust;
295 sal_uInt16 m_nHeight; // options of <TR>/<TD>
296 sal_uInt16 m_nEmptyRows; // number of empty rows are following
297 sal_Int16 m_eVertOri;
298 bool m_bIsEndOfGroup : 1;
299 bool m_bBottomBorder : 1; // Is there a line after the row?
301 public:
303 explicit HTMLTableRow( sal_uInt16 nCells ); // cells of the row are empty
305 void SetBottomBorder(bool bIn) { m_bBottomBorder = bIn; }
306 bool GetBottomBorder() const { return m_bBottomBorder; }
308 inline void SetHeight( sal_uInt16 nHeight );
309 sal_uInt16 GetHeight() const { return m_nHeight; }
311 const HTMLTableCell& GetCell(sal_uInt16 nCell) const;
312 HTMLTableCell& GetCell(sal_uInt16 nCell)
314 return const_cast<HTMLTableCell&>(const_cast<const HTMLTableRow&>(*this).GetCell(nCell));
317 void SetAdjust( SvxAdjust eAdj ) { m_eAdjust = eAdj; }
318 SvxAdjust GetAdjust() const { return m_eAdjust; }
320 void SetVertOri( sal_Int16 eV) { m_eVertOri = eV; }
321 sal_Int16 GetVertOri() const { return m_eVertOri; }
323 void SetBGBrush(std::unique_ptr<SvxBrushItem>& rBrush ) { m_xBGBrush = std::move(rBrush); }
324 const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBGBrush; }
326 void SetEndOfGroup() { m_bIsEndOfGroup = true; }
327 bool IsEndOfGroup() const { return m_bIsEndOfGroup; }
329 void IncEmptyRows() { m_nEmptyRows++; }
330 sal_uInt16 GetEmptyRows() const { return m_nEmptyRows; }
332 // Expand row by adding empty cells
333 void Expand( sal_uInt16 nCells, bool bOneCell=false );
335 // Shrink row by deleting empty cells
336 void Shrink( sal_uInt16 nCells );
339 // Column of a HTML table
340 class HTMLTableColumn
342 bool m_bIsEndOfGroup;
344 sal_uInt16 m_nWidth; // options of <COL>
345 bool m_bRelWidth;
347 SvxAdjust m_eAdjust;
348 sal_Int16 m_eVertOri;
350 SwFrameFormat *m_aFrameFormats[6];
352 static inline sal_uInt16 GetFrameFormatIdx( bool bBorderLine,
353 sal_Int16 eVertOri );
355 public:
357 bool m_bLeftBorder; // is there a line before the column
359 HTMLTableColumn();
361 inline void SetWidth( sal_uInt16 nWidth, bool bRelWidth);
363 void SetAdjust( SvxAdjust eAdj ) { m_eAdjust = eAdj; }
364 SvxAdjust GetAdjust() const { return m_eAdjust; }
366 void SetVertOri( sal_Int16 eV) { m_eVertOri = eV; }
367 sal_Int16 GetVertOri() const { return m_eVertOri; }
369 void SetEndOfGroup() { m_bIsEndOfGroup = true; }
370 bool IsEndOfGroup() const { return m_bIsEndOfGroup; }
372 inline void SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
373 sal_Int16 eVertOri );
374 inline SwFrameFormat *GetFrameFormat( bool bBorderLine,
375 sal_Int16 eVertOri ) const;
377 std::unique_ptr<SwHTMLTableLayoutColumn> CreateLayoutInfo();
382 // HTML table
383 typedef std::vector<SdrObject *> SdrObjects;
385 class HTMLTable : public sdr::ObjectUser
387 OUString m_aId;
388 OUString m_aStyle;
389 OUString m_aClass;
390 OUString m_aDir;
392 std::optional<SdrObjects> m_xResizeDrawObjects;// SDR objects
393 std::optional<std::vector<sal_uInt16>> m_xDrawObjectPercentWidths; // column of draw object and its rel. width
395 std::vector<HTMLTableRow> m_aRows; ///< table rows
396 std::vector<HTMLTableColumn> m_aColumns; ///< table columns
398 sal_uInt16 m_nRows; // number of rows
399 sal_uInt16 m_nCols; // number of columns
400 sal_uInt16 m_nFilledColumns; // number of filled columns
402 sal_uInt16 m_nCurrentRow; // current Row
403 sal_uInt16 m_nCurrentColumn; // current Column
405 sal_uInt16 m_nLeftMargin; // Space to the left margin (from paragraph edge)
406 sal_uInt16 m_nRightMargin; // Space to the right margin (from paragraph edge)
408 sal_uInt16 m_nCellPadding; // Space from border to Text
409 sal_uInt16 m_nCellSpacing; // Space between two cells
410 sal_uInt16 m_nHSpace;
411 sal_uInt16 m_nVSpace;
413 sal_uInt16 m_nBoxes; // number of boxes in the table
415 const SwStartNode *m_pPrevStartNode; // the Table-Node or the Start-Node of the section before
416 const SwTable *m_pSwTable; // SW-Table (only on Top-Level)
417 public:
418 std::unique_ptr<SwTableBox> m_xBox1; // TableBox, generated when the Top-Level-Table was build
419 private:
420 SwTableBoxFormat *m_pBoxFormat; // frame::Frame-Format from SwTableBox
421 SwTableLineFormat *m_pLineFormat; // frame::Frame-Format from SwTableLine
422 SwTableLineFormat *m_pLineFrameFormatNoHeight;
423 std::unique_ptr<SvxBrushItem> m_xBackgroundBrush; // background of the table
424 std::unique_ptr<SvxBrushItem> m_xInheritedBackgroundBrush; // "inherited" background of the table
425 const SwStartNode *m_pCaptionStartNode; // Start-Node of the table-caption
426 //lines for the border
427 SvxBorderLine m_aTopBorderLine;
428 SvxBorderLine m_aBottomBorderLine;
429 SvxBorderLine m_aLeftBorderLine;
430 SvxBorderLine m_aRightBorderLine;
431 SvxBorderLine m_aBorderLine;
432 SvxBorderLine m_aInheritedLeftBorderLine;
433 SvxBorderLine m_aInheritedRightBorderLine;
434 bool m_bTopBorder; // is there a line on the top of the table
435 bool m_bRightBorder; // is there a line on the top right of the table
436 bool m_bTopAllowed; // is it allowed to set the border?
437 bool m_bRightAllowed;
438 bool m_bFillerTopBorder; // gets the left/right filler-cell a border on the
439 bool m_bFillerBottomBorder; // top or in the bottom
440 bool m_bInheritedLeftBorder;
441 bool m_bInheritedRightBorder;
442 bool m_bBordersSet; // the border is set already
443 bool m_bForceFrame;
444 bool m_bTableAdjustOfTag; // comes nTableAdjust from <TABLE>?
445 sal_uInt32 m_nHeadlineRepeat; // repeating rows
446 bool m_bIsParentHead;
447 bool m_bHasParentSection;
448 bool m_bHasToFly;
449 bool m_bFixedCols;
450 bool m_bColSpec; // where there COL(GROUP)-elements?
451 bool m_bPercentWidth; // width is declared in %
453 SwHTMLParser *m_pParser; // the current parser
454 std::unique_ptr<HTMLTableCnts> m_xParentContents;
456 std::unique_ptr<HTMLTableContext> m_pContext; // the context of the table
458 std::shared_ptr<SwHTMLTableLayout> m_xLayoutInfo;
460 // the following parameters are from the <TABLE>-Tag
461 sal_uInt16 m_nWidth; // width of the table
462 sal_uInt16 m_nHeight; // absolute height of the table
463 SvxAdjust m_eTableAdjust; // drawing::Alignment of the table
464 sal_Int16 m_eVertOrientation; // Default vertical direction of the cells
465 sal_uInt16 m_nBorder; // width of the external border
466 HTMLTableFrame m_eFrame; // frame around the table
467 HTMLTableRules m_eRules; // frame in the table
468 bool m_bTopCaption; // Caption of the table
470 void InitCtor(const HTMLTableOptions& rOptions);
472 // 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
473 void FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, const HTMLTableCnts *pCnts );
475 // Protects the chosen cell and the cells among
476 void ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan );
478 // Looking for the SwStartNodes of the box ahead
479 // If nRow==nCell==USHRT_MAX, return the last Start-Node of the table.
480 const SwStartNode* GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCell ) const;
482 sal_uInt16 GetTopCellSpace( sal_uInt16 nRow ) const;
483 sal_uInt16 GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const;
485 // Conforming of the frame::Frame-Format of the box
486 void FixFrameFormat( SwTableBox *pBox, sal_uInt16 nRow, sal_uInt16 nCol,
487 sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
488 bool bFirstPara=true, bool bLastPara=true ) const;
490 // Create a table with the content (lines/boxes)
491 void MakeTable_( SwTableBox *pUpper );
493 // Generate a new SwTableBox, which contains a SwStartNode
494 SwTableBox *NewTableBox( const SwStartNode *pStNd,
495 SwTableLine *pUpper ) const;
497 // Generate a SwTableLine from the cells of the rectangle
498 // (nTopRow/nLeftCol) inclusive to (nBottomRow/nRightRow) exclusive
499 SwTableLine *MakeTableLine( SwTableBox *pUpper,
500 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
501 sal_uInt16 nBottomRow, sal_uInt16 nRightCol );
503 // Generate a SwTableBox from the content of the cell
504 SwTableBox *MakeTableBox( SwTableLine *pUpper,
505 HTMLTableCnts *pCnts,
506 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
507 sal_uInt16 nBootomRow, sal_uInt16 nRightCol );
509 // Autolayout-Algorithm
511 // Setting the border with the help of guidelines of the Parent-Table
512 void InheritBorders( const HTMLTable *pParent,
513 sal_uInt16 nRow, sal_uInt16 nCol,
514 sal_uInt16 nRowSpan,
515 bool bFirstPara, bool bLastPara );
517 // Inherit the left and the right border of the surrounding table
518 void InheritVertBorders( const HTMLTable *pParent,
519 sal_uInt16 nCol, sal_uInt16 nColSpan );
521 // Set the border with the help of the information from the user
522 void SetBorders();
524 // is the border already set?
525 bool BordersSet() const { return m_bBordersSet; }
527 const std::unique_ptr<SvxBrushItem>& GetBGBrush() const { return m_xBackgroundBrush; }
528 const std::unique_ptr<SvxBrushItem>& GetInhBGBrush() const { return m_xInheritedBackgroundBrush; }
530 sal_uInt16 GetBorderWidth( const SvxBorderLine& rBLine,
531 bool bWithDistance=false ) const;
533 virtual void ObjectInDestruction(const SdrObject& rObject) override;
535 public:
537 bool m_bFirstCell; // is there a cell created already?
539 HTMLTable(SwHTMLParser* pPars,
540 bool bParHead, bool bHasParentSec,
541 bool bHasToFly,
542 const HTMLTableOptions& rOptions);
544 virtual ~HTMLTable();
546 // Identifying of a cell
547 const HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const;
548 HTMLTableCell& GetCell(sal_uInt16 nRow, sal_uInt16 nCell)
550 return const_cast<HTMLTableCell&>(const_cast<const HTMLTable&>(*this).GetCell(nRow, nCell));
553 // set/determine caption
554 inline void SetCaption( const SwStartNode *pStNd, bool bTop );
555 const SwStartNode *GetCaptionStartNode() const { return m_pCaptionStartNode; }
556 bool IsTopCaption() const { return m_bTopCaption; }
558 SvxAdjust GetTableAdjust( bool bAny ) const
560 return (m_bTableAdjustOfTag || bAny) ? m_eTableAdjust : SvxAdjust::End;
563 sal_uInt16 GetHSpace() const { return m_nHSpace; }
564 sal_uInt16 GetVSpace() const { return m_nVSpace; }
566 // get inherited drawing::Alignment of rows and column
567 SvxAdjust GetInheritedAdjust() const;
568 sal_Int16 GetInheritedVertOri() const;
570 // Insert a cell on the current position
571 void InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
572 sal_uInt16 nWidth, bool bRelWidth, sal_uInt16 nHeight,
573 sal_Int16 eVertOri, std::shared_ptr<SvxBrushItem> const& rBGBrushItem,
574 std::shared_ptr<SvxBoxItem> const& rBoxItem,
575 bool bHasNumFormat, sal_uInt32 nNumFormat,
576 bool bHasValue, double nValue, bool bNoWrap );
578 // announce the start/end of a new row
579 void OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOri, std::unique_ptr<SvxBrushItem>& rBGBrush);
580 void CloseRow( bool bEmpty );
582 // announce the end of a new section
583 inline void CloseSection( bool bHead );
585 // announce the end of a column-group
586 inline void CloseColGroup( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
587 SvxAdjust eAdjust, sal_Int16 eVertOri );
589 // insert a new column
590 void InsertCol( sal_uInt16 nSpan, sal_uInt16 nWidth, bool bRelWidth,
591 SvxAdjust eAdjust, sal_Int16 eVertOri );
593 // End a table definition (needs to be called for every table)
594 void CloseTable();
596 // Construct a SwTable (including child tables)
597 void MakeTable( SwTableBox *pUpper, sal_uInt16 nAbsAvail,
598 sal_uInt16 nRelAvail=0, sal_uInt16 nAbsLeftSpace=0,
599 sal_uInt16 nAbsRightSpace=0, sal_uInt16 nInhAbsSpace=0 );
601 bool IsNewDoc() const { return m_pParser->IsNewDoc(); }
603 void SetHasParentSection( bool bSet ) { m_bHasParentSection = bSet; }
604 bool HasParentSection() const { return m_bHasParentSection; }
606 void SetParentContents(std::unique_ptr<HTMLTableCnts> pCnts) { m_xParentContents = std::move(pCnts); }
607 std::unique_ptr<HTMLTableCnts>& GetParentContents() { return m_xParentContents; }
609 void MakeParentContents();
611 bool HasToFly() const { return m_bHasToFly; }
613 void SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt,
614 sal_uInt16 nLeft, sal_uInt16 nRight,
615 const SwTable *pSwTab=nullptr, bool bFrcFrame=false );
617 HTMLTableContext *GetContext() const { return m_pContext.get(); }
619 const std::shared_ptr<SwHTMLTableLayout>& CreateLayoutInfo();
621 bool HasColTags() const { return m_bColSpec; }
623 sal_uInt16 IncGrfsThatResize() { return m_pSwTable ? const_cast<SwTable *>(m_pSwTable)->IncGrfsThatResize() : 0; }
625 void RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth );
627 const SwTable *GetSwTable() const { return m_pSwTable; }
629 void SetBGBrush(const SvxBrushItem& rBrush) { m_xBackgroundBrush.reset(new SvxBrushItem(rBrush)); }
631 const OUString& GetId() const { return m_aId; }
632 const OUString& GetClass() const { return m_aClass; }
633 const OUString& GetStyle() const { return m_aStyle; }
634 const OUString& GetDirection() const { return m_aDir; }
636 void IncBoxCount() { m_nBoxes++; }
637 bool IsOverflowing() const { return m_nBoxes > 64000; }
640 void HTMLTableCnts::InitCtor()
642 m_pNext = nullptr;
643 m_xLayoutInfo.reset();
644 m_bNoBreak = false;
647 HTMLTableCnts::HTMLTableCnts(const SwStartNode* pStNd)
648 : m_pStartNode(pStNd)
650 InitCtor();
653 HTMLTableCnts::HTMLTableCnts(std::shared_ptr<HTMLTable> xTab)
654 : m_pStartNode(nullptr)
655 , m_xTable(std::move(xTab))
657 InitCtor();
660 HTMLTableCnts::~HTMLTableCnts()
662 m_xTable.reset(); // we don't need the tables anymore
663 m_pNext.reset();
666 void HTMLTableCnts::Add( std::unique_ptr<HTMLTableCnts> pNewCnts )
668 HTMLTableCnts *pCnts = this;
670 while( pCnts->m_pNext )
671 pCnts = pCnts->m_pNext.get();
673 pCnts->m_pNext = std::move(pNewCnts);
676 inline void HTMLTableCnts::SetTableBox( SwTableBox *pBox )
678 OSL_ENSURE(m_xLayoutInfo, "There is no layout info");
679 if (m_xLayoutInfo)
680 m_xLayoutInfo->SetTableBox(pBox);
683 const std::shared_ptr<SwHTMLTableLayoutCnts>& HTMLTableCnts::CreateLayoutInfo()
685 if (!m_xLayoutInfo)
687 std::shared_ptr<SwHTMLTableLayoutCnts> xNextInfo;
688 if (m_pNext)
689 xNextInfo = m_pNext->CreateLayoutInfo();
690 std::shared_ptr<SwHTMLTableLayout> xTableInfo;
691 if (m_xTable)
692 xTableInfo = m_xTable->CreateLayoutInfo();
693 m_xLayoutInfo = std::make_shared<SwHTMLTableLayoutCnts>(m_pStartNode, xTableInfo, m_bNoBreak, xNextInfo);
696 return m_xLayoutInfo;
699 HTMLTableCell::HTMLTableCell():
700 m_nValue(0),
701 m_nNumFormat(0),
702 m_nRowSpan(1),
703 m_nColSpan(1),
704 m_nWidth( 0 ),
705 m_eVertOrient( text::VertOrientation::NONE ),
706 m_bProtected(false),
707 m_bRelWidth( false ),
708 m_bHasNumFormat(false),
709 m_bHasValue(false),
710 m_bNoWrap(false),
711 mbCovered(false)
714 void HTMLTableCell::Set( std::shared_ptr<HTMLTableCnts> const& rCnts, sal_uInt16 nRSpan, sal_uInt16 nCSpan,
715 sal_Int16 eVert, std::shared_ptr<SvxBrushItem> const& rBrush,
716 std::shared_ptr<SvxBoxItem> const& rBoxItem,
717 bool bHasNF, sal_uInt32 nNF, bool bHasV, double nVal,
718 bool bNWrap, bool bCovered )
720 m_xContents = rCnts;
721 m_nRowSpan = nRSpan;
722 m_nColSpan = nCSpan;
723 m_bProtected = false;
724 m_eVertOrient = eVert;
725 m_xBGBrush = rBrush;
726 m_xBoxItem = rBoxItem;
728 m_bHasNumFormat = bHasNF;
729 m_bHasValue = bHasV;
730 m_nNumFormat = nNF;
731 m_nValue = nVal;
733 m_bNoWrap = bNWrap;
734 mbCovered = bCovered;
737 inline void HTMLTableCell::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
739 m_nWidth = nWdth;
740 m_bRelWidth = bRelWdth;
743 void HTMLTableCell::SetProtected()
745 // The content of this cell doesn't have to be anchored anywhere else,
746 // since they're not gonna be deleted
748 m_xContents.reset();
750 // Copy background color
751 if (m_xBGBrush)
752 m_xBGBrush = std::make_shared<SvxBrushItem>(*m_xBGBrush);
754 m_nRowSpan = 1;
755 m_nColSpan = 1;
756 m_bProtected = true;
759 inline bool HTMLTableCell::GetNumFormat( sal_uInt32& rNumFormat ) const
761 rNumFormat = m_nNumFormat;
762 return m_bHasNumFormat;
765 inline bool HTMLTableCell::GetValue( double& rValue ) const
767 rValue = m_nValue;
768 return m_bHasValue;
771 std::unique_ptr<SwHTMLTableLayoutCell> HTMLTableCell::CreateLayoutInfo()
773 std::shared_ptr<SwHTMLTableLayoutCnts> xCntInfo;
774 if (m_xContents)
775 xCntInfo = m_xContents->CreateLayoutInfo();
776 return std::unique_ptr<SwHTMLTableLayoutCell>(new SwHTMLTableLayoutCell(xCntInfo, m_nRowSpan, m_nColSpan, m_nWidth,
777 m_bRelWidth, m_bNoWrap));
780 HTMLTableRow::HTMLTableRow(sal_uInt16 const nCells)
781 : m_aCells(nCells)
782 , m_eAdjust(SvxAdjust::End)
783 , m_nHeight(0)
784 , m_nEmptyRows(0)
785 , m_eVertOri(text::VertOrientation::TOP)
786 , m_bIsEndOfGroup(false)
787 , m_bBottomBorder(false)
789 assert(nCells == m_aCells.size() &&
790 "wrong Cell count in new HTML table row");
793 inline void HTMLTableRow::SetHeight( sal_uInt16 nHght )
795 if( nHght > m_nHeight )
796 m_nHeight = nHght;
799 const HTMLTableCell& HTMLTableRow::GetCell(sal_uInt16 nCell) const
801 OSL_ENSURE( nCell < m_aCells.size(),
802 "invalid cell index in HTML table row" );
803 return m_aCells.at(nCell);
806 void HTMLTableRow::Expand( sal_uInt16 nCells, bool bOneCell )
808 // This row will be filled with a single cell if bOneCell is set.
809 // This will only work for rows that don't allow adding cells!
811 sal_uInt16 nColSpan = nCells - m_aCells.size();
812 for (sal_uInt16 i = m_aCells.size(); i < nCells; ++i)
814 m_aCells.emplace_back();
815 if (bOneCell)
816 m_aCells.back().SetColSpan(nColSpan);
817 --nColSpan;
820 OSL_ENSURE(nCells == m_aCells.size(),
821 "wrong Cell count in expanded HTML table row");
824 void HTMLTableRow::Shrink( sal_uInt16 nCells )
826 OSL_ENSURE(nCells < m_aCells.size(), "number of cells too large");
828 #if OSL_DEBUG_LEVEL > 0
829 sal_uInt16 const nEnd = m_aCells.size();
830 #endif
831 // The colspan of empty cells at the end has to be fixed to the new
832 // number of cells.
833 sal_uInt16 i=nCells;
834 while( i )
836 HTMLTableCell& rCell = m_aCells[--i];
837 if (!rCell.GetContents())
839 #if OSL_DEBUG_LEVEL > 0
840 OSL_ENSURE( rCell.GetColSpan() == nEnd - i,
841 "invalid col span for empty cell at row end" );
842 #endif
843 rCell.SetColSpan( nCells-i);
845 else
846 break;
848 #if OSL_DEBUG_LEVEL > 0
849 for( i=nCells; i<nEnd; i++ )
851 HTMLTableCell& rCell = m_aCells[i];
852 OSL_ENSURE( rCell.GetRowSpan() == 1,
853 "RowSpan of to be deleted cell is wrong" );
854 OSL_ENSURE( rCell.GetColSpan() == nEnd - i,
855 "ColSpan of to be deleted cell is wrong" );
856 OSL_ENSURE( !rCell.GetContents(), "To be deleted cell has content" );
858 #endif
860 m_aCells.erase(m_aCells.begin() + nCells, m_aCells.end());
863 HTMLTableColumn::HTMLTableColumn():
864 m_bIsEndOfGroup(false),
865 m_nWidth(0), m_bRelWidth(false),
866 m_eAdjust(SvxAdjust::End), m_eVertOri(text::VertOrientation::TOP),
867 m_bLeftBorder(false)
869 for(SwFrameFormat* & rp : m_aFrameFormats)
870 rp = nullptr;
873 inline void HTMLTableColumn::SetWidth( sal_uInt16 nWdth, bool bRelWdth )
875 if( m_bRelWidth==bRelWdth )
877 if( nWdth > m_nWidth )
878 m_nWidth = nWdth;
880 else
881 m_nWidth = nWdth;
882 m_bRelWidth = bRelWdth;
885 inline std::unique_ptr<SwHTMLTableLayoutColumn> HTMLTableColumn::CreateLayoutInfo()
887 return std::unique_ptr<SwHTMLTableLayoutColumn>(new SwHTMLTableLayoutColumn( m_nWidth, m_bRelWidth, m_bLeftBorder ));
890 inline sal_uInt16 HTMLTableColumn::GetFrameFormatIdx( bool bBorderLine,
891 sal_Int16 eVertOrient )
893 OSL_ENSURE( text::VertOrientation::TOP != eVertOrient, "Top is not allowed" );
894 sal_uInt16 n = bBorderLine ? 3 : 0;
895 switch( eVertOrient )
897 case text::VertOrientation::CENTER: n+=1; break;
898 case text::VertOrientation::BOTTOM: n+=2; break;
899 default:
902 return n;
905 inline void HTMLTableColumn::SetFrameFormat( SwFrameFormat *pFormat, bool bBorderLine,
906 sal_Int16 eVertOrient )
908 m_aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)] = pFormat;
911 inline SwFrameFormat *HTMLTableColumn::GetFrameFormat( bool bBorderLine,
912 sal_Int16 eVertOrient ) const
914 return m_aFrameFormats[GetFrameFormatIdx(bBorderLine,eVertOrient)];
917 void HTMLTable::InitCtor(const HTMLTableOptions& rOptions)
919 m_nRows = 0;
920 m_nCurrentRow = 0; m_nCurrentColumn = 0;
922 m_pBoxFormat = nullptr; m_pLineFormat = nullptr;
923 m_pLineFrameFormatNoHeight = nullptr;
924 m_xInheritedBackgroundBrush.reset();
926 m_pPrevStartNode = nullptr;
927 m_pSwTable = nullptr;
929 m_bTopBorder = false; m_bRightBorder = false;
930 m_bTopAllowed = true; m_bRightAllowed = true;
931 m_bFillerTopBorder = false; m_bFillerBottomBorder = false;
932 m_bInheritedLeftBorder = false; m_bInheritedRightBorder = false;
933 m_bBordersSet = false;
934 m_bForceFrame = false;
935 m_nHeadlineRepeat = 0;
937 m_nLeftMargin = 0;
938 m_nRightMargin = 0;
940 const Color& rBorderColor = rOptions.aBorderColor;
942 tools::Long nBorderOpt = static_cast<tools::Long>(rOptions.nBorder);
943 tools::Long nPWidth = nBorderOpt==USHRT_MAX ? NETSCAPE_DFLT_BORDER
944 : nBorderOpt;
945 tools::Long nPHeight = nBorderOpt==USHRT_MAX ? 0 : nBorderOpt;
946 SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
948 // nBorder tells the width of the border as it's used in the width calculation of NetScape
949 // If pOption->nBorder == USHRT_MAX, there wasn't a BORDER option given
950 // Nonetheless, a 1 pixel wide border will be used for width calculation
951 m_nBorder = o3tl::narrowing<sal_uInt16>(nPWidth);
952 if( nBorderOpt==USHRT_MAX )
953 nPWidth = 0;
955 if ( rOptions.nCellSpacing != 0 )
957 m_aTopBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
959 m_aTopBorderLine.SetWidth( nPHeight );
960 m_aTopBorderLine.SetColor( rBorderColor );
961 m_aBottomBorderLine = m_aTopBorderLine;
963 if( nPWidth == nPHeight )
965 m_aLeftBorderLine = m_aTopBorderLine;
967 else
969 if ( rOptions.nCellSpacing != 0 )
971 m_aLeftBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
973 m_aLeftBorderLine.SetWidth( nPWidth );
974 m_aLeftBorderLine.SetColor( rBorderColor );
976 m_aRightBorderLine = m_aLeftBorderLine;
978 if( rOptions.nCellSpacing != 0 )
979 m_aBorderLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
980 m_aBorderLine.SetWidth(SvxBorderLineWidth::Hairline);
981 m_aBorderLine.SetColor( rBorderColor );
983 if( m_nCellPadding )
985 if( m_nCellPadding==USHRT_MAX )
986 m_nCellPadding = MIN_BORDER_DIST; // default
987 else
989 m_nCellPadding = SwHTMLParser::ToTwips( m_nCellPadding );
990 if( m_nCellPadding<MIN_BORDER_DIST )
991 m_nCellPadding = MIN_BORDER_DIST;
994 if( m_nCellSpacing )
996 if( m_nCellSpacing==USHRT_MAX )
997 m_nCellSpacing = NETSCAPE_DFLT_CELLSPACING;
998 m_nCellSpacing = SwHTMLParser::ToTwips( m_nCellSpacing );
1001 nPWidth = rOptions.nHSpace;
1002 nPHeight = rOptions.nVSpace;
1003 SvxCSS1Parser::PixelToTwip( nPWidth, nPHeight );
1004 m_nHSpace = o3tl::narrowing<sal_uInt16>(nPWidth);
1005 m_nVSpace = o3tl::narrowing<sal_uInt16>(nPHeight);
1007 m_bColSpec = false;
1009 m_xBackgroundBrush.reset(m_pParser->CreateBrushItem(
1010 rOptions.bBGColor ? &(rOptions.aBGColor) : nullptr,
1011 rOptions.aBGImage, OUString(), OUString(), OUString()));
1013 m_pContext = nullptr;
1014 m_xParentContents.reset();
1016 m_aId = rOptions.aId;
1017 m_aClass = rOptions.aClass;
1018 m_aStyle = rOptions.aStyle;
1019 m_aDir = rOptions.aDir;
1022 HTMLTable::HTMLTable(SwHTMLParser* pPars,
1023 bool bParHead,
1024 bool bHasParentSec, bool bHasToFlw,
1025 const HTMLTableOptions& rOptions) :
1026 m_aColumns(rOptions.nCols),
1027 m_nCols(rOptions.nCols),
1028 m_nFilledColumns( 0 ),
1029 m_nCellPadding(rOptions.nCellPadding),
1030 m_nCellSpacing(rOptions.nCellSpacing),
1031 m_nBoxes( 1 ),
1032 m_pCaptionStartNode( nullptr ),
1033 m_bTableAdjustOfTag( rOptions.bTableAdjust ),
1034 m_bIsParentHead( bParHead ),
1035 m_bHasParentSection( bHasParentSec ),
1036 m_bHasToFly( bHasToFlw ),
1037 m_bFixedCols( rOptions.nCols>0 ),
1038 m_bPercentWidth( rOptions.bPercentWidth ),
1039 m_pParser( pPars ),
1040 m_nWidth( rOptions.nWidth ),
1041 m_nHeight( rOptions.nHeight ),
1042 m_eTableAdjust( rOptions.eAdjust ),
1043 m_eVertOrientation( rOptions.eVertOri ),
1044 m_eFrame( rOptions.eFrame ),
1045 m_eRules( rOptions.eRules ),
1046 m_bTopCaption( false ),
1047 m_bFirstCell(true)
1049 InitCtor(rOptions);
1050 m_pParser->RegisterHTMLTable(this);
1053 void SwHTMLParser::DeregisterHTMLTable(HTMLTable* pOld)
1055 if (pOld->m_xBox1)
1056 m_aOrphanedTableBoxes.emplace_back(std::move(pOld->m_xBox1));
1057 m_aTables.erase(std::remove(m_aTables.begin(), m_aTables.end(), pOld));
1060 SwDoc* SwHTMLParser::GetDoc() const
1062 return m_xDoc.get();
1065 bool SwHTMLParser::IsReqIF() const
1067 return m_bReqIF;
1070 // if any m_xResizeDrawObjects members are deleted during parse, remove them
1071 // from m_xResizeDrawObjects and m_xDrawObjectPercentWidths
1072 void HTMLTable::ObjectInDestruction(const SdrObject& rObject)
1074 auto it = std::find(m_xResizeDrawObjects->begin(), m_xResizeDrawObjects->end(), &rObject);
1075 assert(it != m_xResizeDrawObjects->end());
1076 auto nIndex = std::distance(m_xResizeDrawObjects->begin(), it);
1077 m_xResizeDrawObjects->erase(it);
1078 auto otherit = m_xDrawObjectPercentWidths->begin() + nIndex * 3;
1079 m_xDrawObjectPercentWidths->erase(otherit, otherit + 3);
1082 HTMLTable::~HTMLTable()
1084 m_pParser->DeregisterHTMLTable(this);
1086 if (m_xResizeDrawObjects)
1088 size_t nCount = m_xResizeDrawObjects->size();
1089 for (size_t i = 0; i < nCount; ++i)
1091 SdrObject *pObj = (*m_xResizeDrawObjects)[i];
1092 pObj->RemoveObjectUser(*this);
1094 m_xResizeDrawObjects.reset();
1097 m_xDrawObjectPercentWidths.reset();
1099 m_pContext.reset();
1101 // pLayoutInfo has either already been deleted or is now owned by SwTable
1104 const std::shared_ptr<SwHTMLTableLayout>& HTMLTable::CreateLayoutInfo()
1106 sal_uInt16 nW = m_bPercentWidth ? m_nWidth : SwHTMLParser::ToTwips( m_nWidth );
1108 sal_uInt16 nBorderWidth = GetBorderWidth( m_aBorderLine, true );
1109 sal_uInt16 nLeftBorderWidth =
1110 m_aColumns[0].m_bLeftBorder ? GetBorderWidth(m_aLeftBorderLine, true) : 0;
1111 sal_uInt16 nRightBorderWidth =
1112 m_bRightBorder ? GetBorderWidth( m_aRightBorderLine, true ) : 0;
1114 m_xLayoutInfo = std::make_shared<SwHTMLTableLayout>(
1115 m_pSwTable,
1116 m_nRows, m_nCols, m_bFixedCols, m_bColSpec,
1117 nW, m_bPercentWidth, m_nBorder, m_nCellPadding,
1118 m_nCellSpacing, m_eTableAdjust,
1119 m_nLeftMargin, m_nRightMargin,
1120 nBorderWidth, nLeftBorderWidth, nRightBorderWidth);
1122 bool bExportable = true;
1123 sal_uInt16 i;
1124 for( i=0; i<m_nRows; i++ )
1126 HTMLTableRow& rRow = m_aRows[i];
1127 for( sal_uInt16 j=0; j<m_nCols; j++ )
1129 m_xLayoutInfo->SetCell(rRow.GetCell(j).CreateLayoutInfo(), i, j);
1130 SwHTMLTableLayoutCell* pLayoutCell = m_xLayoutInfo->GetCell(i, j );
1132 if( bExportable )
1134 const std::shared_ptr<SwHTMLTableLayoutCnts>& rLayoutCnts =
1135 pLayoutCell->GetContents();
1136 bExportable = !rLayoutCnts ||
1137 (rLayoutCnts->GetStartNode() && !rLayoutCnts->GetNext());
1142 m_xLayoutInfo->SetExportable( bExportable );
1144 for( i=0; i<m_nCols; i++ )
1145 m_xLayoutInfo->SetColumn(m_aColumns[i].CreateLayoutInfo(), i);
1147 return m_xLayoutInfo;
1150 inline void HTMLTable::SetCaption( const SwStartNode *pStNd, bool bTop )
1152 m_pCaptionStartNode = pStNd;
1153 m_bTopCaption = bTop;
1156 void HTMLTable::FixRowSpan( sal_uInt16 nRow, sal_uInt16 nCol,
1157 const HTMLTableCnts *pCnts )
1159 sal_uInt16 nRowSpan=1;
1160 while (true)
1162 HTMLTableCell& rCell = GetCell(nRow, nCol);
1163 if (rCell.GetContents().get() != pCnts)
1164 break;
1165 rCell.SetRowSpan(nRowSpan);
1166 if (m_xLayoutInfo)
1167 m_xLayoutInfo->GetCell(nRow,nCol)->SetRowSpan(nRowSpan);
1169 if( !nRow ) break;
1170 nRowSpan++; nRow--;
1174 void HTMLTable::ProtectRowSpan( sal_uInt16 nRow, sal_uInt16 nCol, sal_uInt16 nRowSpan )
1176 for( sal_uInt16 i=0; i<nRowSpan; i++ )
1178 GetCell(nRow+i,nCol).SetProtected();
1179 if (m_xLayoutInfo)
1180 m_xLayoutInfo->GetCell(nRow+i,nCol)->SetProtected();
1184 // Search the SwStartNode of the last used predecessor box
1185 const SwStartNode* HTMLTable::GetPrevBoxStartNode( sal_uInt16 nRow, sal_uInt16 nCol ) const
1187 const HTMLTableCnts *pPrevCnts = nullptr;
1189 if( 0==nRow )
1191 // always the predecessor cell
1192 if( nCol>0 )
1193 pPrevCnts = GetCell(0, nCol - 1).GetContents().get();
1194 else
1195 return m_pPrevStartNode;
1197 else if( USHRT_MAX==nRow && USHRT_MAX==nCol )
1198 // contents of preceding cell
1199 pPrevCnts = GetCell(m_nRows - 1, m_nCols - 1).GetContents().get();
1200 else
1202 sal_uInt16 i;
1203 const HTMLTableRow& rPrevRow = m_aRows[nRow-1];
1205 // maybe a cell in the current row
1206 i = nCol;
1207 while( i )
1209 i--;
1210 if( 1 == rPrevRow.GetCell(i).GetRowSpan() )
1212 pPrevCnts = GetCell(nRow, i).GetContents().get();
1213 break;
1217 // otherwise the last filled cell of the row before
1218 if( !pPrevCnts )
1220 i = m_nCols;
1221 while( !pPrevCnts && i )
1223 i--;
1224 pPrevCnts = rPrevRow.GetCell(i).GetContents().get();
1228 OSL_ENSURE( pPrevCnts, "No previous filled cell found" );
1229 if( !pPrevCnts )
1231 pPrevCnts = GetCell(0, 0).GetContents().get();
1232 if( !pPrevCnts )
1233 return m_pPrevStartNode;
1236 while( pPrevCnts->Next() )
1237 pPrevCnts = pPrevCnts->Next();
1239 const SwStartNode* pRet = pPrevCnts->GetStartNode();
1240 if (pRet)
1241 return pRet;
1242 HTMLTable* pTable = pPrevCnts->GetTable().get();
1243 if (!pTable)
1244 return nullptr;
1245 return pTable->GetPrevBoxStartNode(USHRT_MAX, USHRT_MAX);
1248 sal_uInt16 HTMLTable::GetTopCellSpace( sal_uInt16 nRow ) const
1250 sal_uInt16 nSpace = m_nCellPadding;
1252 if( nRow == 0 )
1254 nSpace += m_nBorder + m_nCellSpacing;
1257 return nSpace;
1260 sal_uInt16 HTMLTable::GetBottomCellSpace( sal_uInt16 nRow, sal_uInt16 nRowSpan ) const
1262 sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
1264 if( nRow+nRowSpan == m_nRows )
1266 nSpace = nSpace + m_nBorder;
1269 return nSpace;
1272 void HTMLTable::FixFrameFormat( SwTableBox *pBox,
1273 sal_uInt16 nRow, sal_uInt16 nCol,
1274 sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
1275 bool bFirstPara, bool bLastPara ) const
1277 SwFrameFormat *pFrameFormat = nullptr; // frame::Frame format
1278 sal_Int16 eVOri = text::VertOrientation::NONE;
1279 const SvxBrushItem *pBGBrushItem = nullptr; // background
1280 std::shared_ptr<SvxBoxItem> pBoxItem;
1281 bool bTopLine = false, bBottomLine = false, bLastBottomLine = false;
1282 bool bReUsable = false; // Format reusable?
1283 sal_uInt16 nEmptyRows = 0;
1284 bool bHasNumFormat = false;
1285 bool bHasValue = false;
1286 sal_uInt32 nNumFormat = 0;
1287 double nValue = 0.0;
1289 const HTMLTableColumn& rColumn = m_aColumns[nCol];
1291 if( pBox->GetSttNd() )
1293 // Determine background color/graphic
1294 const HTMLTableCell& rCell = GetCell(nRow, nCol);
1295 pBoxItem = rCell.GetBoxItem();
1296 pBGBrushItem = rCell.GetBGBrush().get();
1297 if( !pBGBrushItem )
1299 // If a cell spans multiple rows, a background to that row should be copied to the cell.
1300 if (nRowSpan > 1)
1302 pBGBrushItem = m_aRows[nRow].GetBGBrush().get();
1306 bTopLine = 0==nRow && m_bTopBorder && bFirstPara;
1307 if (m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
1309 nEmptyRows = m_aRows[nRow+nRowSpan-1].GetEmptyRows();
1310 if( nRow+nRowSpan == m_nRows )
1311 bLastBottomLine = true;
1312 else
1313 bBottomLine = true;
1316 eVOri = rCell.GetVertOri();
1317 bHasNumFormat = rCell.GetNumFormat( nNumFormat );
1318 if( bHasNumFormat )
1319 bHasValue = rCell.GetValue( nValue );
1321 if( nColSpan==1 && !bTopLine && !bLastBottomLine && !nEmptyRows &&
1322 !pBGBrushItem && !bHasNumFormat && !pBoxItem)
1324 pFrameFormat = rColumn.GetFrameFormat( bBottomLine, eVOri );
1325 bReUsable = !pFrameFormat;
1329 if( !pFrameFormat )
1331 pFrameFormat = pBox->ClaimFrameFormat();
1333 // calculate width of the box
1334 SwTwips nFrameWidth = static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol)
1335 ->GetRelColWidth());
1336 for( sal_uInt16 i=1; i<nColSpan; i++ )
1337 nFrameWidth += static_cast<SwTwips>(m_xLayoutInfo->GetColumn(nCol+i)
1338 ->GetRelColWidth());
1340 // Only set the border on edit boxes.
1341 // On setting the upper and lower border, keep in mind if
1342 // it's the first or the last paragraph of the cell
1343 if( pBox->GetSttNd() )
1345 bool bSet = (m_nCellPadding > 0);
1347 SvxBoxItem aBoxItem( RES_BOX );
1348 tools::Long nInnerFrameWidth = nFrameWidth;
1350 if( bTopLine )
1352 aBoxItem.SetLine( &m_aTopBorderLine, SvxBoxItemLine::TOP );
1353 bSet = true;
1355 if( bLastBottomLine )
1357 aBoxItem.SetLine( &m_aBottomBorderLine, SvxBoxItemLine::BOTTOM );
1358 bSet = true;
1360 else if( bBottomLine )
1362 if( nEmptyRows && !m_aBorderLine.GetInWidth() )
1364 // For now, empty rows can only be emulated by thick lines, if it's a single line
1365 SvxBorderLine aThickBorderLine( m_aBorderLine );
1367 sal_uInt16 nBorderWidth = m_aBorderLine.GetOutWidth();
1368 nBorderWidth *= (nEmptyRows + 1);
1369 aThickBorderLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
1370 aThickBorderLine.SetWidth( nBorderWidth );
1371 aBoxItem.SetLine( &aThickBorderLine, SvxBoxItemLine::BOTTOM );
1373 else
1375 aBoxItem.SetLine( &m_aBorderLine, SvxBoxItemLine::BOTTOM );
1377 bSet = true;
1379 if (m_aColumns[nCol].m_bLeftBorder)
1381 const SvxBorderLine& rBorderLine =
1382 0==nCol ? m_aLeftBorderLine : m_aBorderLine;
1383 aBoxItem.SetLine( &rBorderLine, SvxBoxItemLine::LEFT );
1384 nInnerFrameWidth -= GetBorderWidth( rBorderLine );
1385 bSet = true;
1387 if( m_bRightBorder )
1389 aBoxItem.SetLine( &m_aRightBorderLine, SvxBoxItemLine::RIGHT );
1390 nInnerFrameWidth -= GetBorderWidth( m_aRightBorderLine );
1391 bSet = true;
1394 if (pBoxItem)
1396 pFrameFormat->SetFormatAttr( *pBoxItem );
1398 else if (bSet)
1400 // BorderDist is not part of a cell with fixed width
1401 sal_uInt16 nBDist = static_cast< sal_uInt16 >(
1402 (2*m_nCellPadding <= nInnerFrameWidth) ? m_nCellPadding
1403 : (nInnerFrameWidth / 2) );
1404 // We only set the item if there's a border or a border distance
1405 // If the latter is missing, there's gonna be a border and we'll have to set the distance
1406 aBoxItem.SetAllDistances(nBDist ? nBDist : MIN_BORDER_DIST);
1407 pFrameFormat->SetFormatAttr( aBoxItem );
1409 else
1410 pFrameFormat->ResetFormatAttr( RES_BOX );
1412 if( pBGBrushItem )
1414 pFrameFormat->SetFormatAttr( *pBGBrushItem );
1416 else
1417 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1419 // Only set format if there's a value or the box is empty
1420 if( bHasNumFormat && (bHasValue || pBox->IsEmpty()) )
1422 bool bLock = pFrameFormat->GetDoc()->GetNumberFormatter()
1423 ->IsTextFormat( nNumFormat );
1424 SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE>
1425 aItemSet( *pFrameFormat->GetAttrSet().GetPool() );
1426 SvxAdjust eAdjust = SvxAdjust::End;
1427 SwContentNode *pCNd = nullptr;
1428 if( !bLock )
1430 const SwStartNode *pSttNd = pBox->GetSttNd();
1431 pCNd = pSttNd->GetNodes()[pSttNd->GetIndex()+1]
1432 ->GetContentNode();
1433 const SvxAdjustItem *pItem;
1434 if( pCNd && pCNd->HasSwAttrSet() &&
1435 (pItem = pCNd->GetpSwAttrSet()->GetItemIfSet(
1436 RES_PARATR_ADJUST, false )) )
1438 eAdjust = pItem->GetAdjust();
1441 aItemSet.Put( SwTableBoxNumFormat(nNumFormat) );
1442 if( bHasValue )
1443 aItemSet.Put( SwTableBoxValue(nValue) );
1445 if( bLock )
1446 pFrameFormat->LockModify();
1447 pFrameFormat->SetFormatAttr( aItemSet );
1448 if( bLock )
1449 pFrameFormat->UnlockModify();
1450 else if( pCNd && SvxAdjust::End != eAdjust )
1452 SvxAdjustItem aAdjItem( eAdjust, RES_PARATR_ADJUST );
1453 pCNd->SetAttr( aAdjItem );
1456 else
1457 pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1459 OSL_ENSURE( eVOri != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
1460 if( text::VertOrientation::NONE != eVOri )
1462 pFrameFormat->SetFormatAttr( SwFormatVertOrient( 0, eVOri ) );
1464 else
1465 pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
1467 if( bReUsable )
1468 const_cast<HTMLTableColumn&>(rColumn).SetFrameFormat(pFrameFormat, bBottomLine, eVOri);
1470 else
1472 pFrameFormat->ResetFormatAttr( RES_BOX );
1473 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1474 pFrameFormat->ResetFormatAttr( RES_VERT_ORIENT );
1475 pFrameFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
1478 if (m_pParser->IsReqIF())
1480 // ReqIF case, cells would have no formatting. Apply the default
1481 // table autoformat on them, so imported and UI-created tables look
1482 // the same.
1483 SwTableAutoFormatTable& rTable = m_pParser->GetDoc()->GetTableStyles();
1484 SwTableAutoFormat* pTableFormat = rTable.FindAutoFormat(
1485 SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_DEFAULT, OUString()));
1486 if (pTableFormat)
1488 sal_uInt8 nPos = SwTableAutoFormat::CountPos(nCol, m_nCols, nRow, m_nRows);
1489 const SfxItemSet& rAttrSet = pFrameFormat->GetAttrSet();
1490 std::unique_ptr<SvxBoxItem> pOldBoxItem;
1491 if (const SvxBoxItem* pBoxItem2 = rAttrSet.GetItemIfSet(RES_BOX))
1492 pOldBoxItem.reset(pBoxItem2->Clone());
1493 pTableFormat->UpdateToSet(nPos, m_nRows==1, m_nCols==1,
1494 const_cast<SfxItemSet&>(rAttrSet),
1495 SwTableAutoFormatUpdateFlags::Box,
1496 pFrameFormat->GetDoc()->GetNumberFormatter());
1497 if (pOldBoxItem)
1499 // There was an old item, so it's guaranteed that there's a new item
1500 const SvxBoxItem* pBoxItem2(rAttrSet.GetItem(RES_BOX));
1501 if (*pBoxItem2 != *pOldBoxItem)
1503 std::unique_ptr<SvxBoxItem> pNewBoxItem(pBoxItem2->Clone());
1504 // Restore the box elements that could have been already set
1505 for (auto eLine : { SvxBoxItemLine::TOP, SvxBoxItemLine::BOTTOM,
1506 SvxBoxItemLine::LEFT, SvxBoxItemLine::RIGHT })
1508 if (auto pLine = pOldBoxItem->GetLine(eLine))
1509 pNewBoxItem->SetLine(pLine, eLine);
1510 if (auto nDistance = pOldBoxItem->GetDistance(eLine, true))
1511 pNewBoxItem->SetDistance(nDistance, eLine);
1514 pFrameFormat->SetFormatAttr(*pNewBoxItem);
1520 else
1522 OSL_ENSURE( pBox->GetSttNd() ||
1523 SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState(
1524 RES_VERT_ORIENT, false ),
1525 "Box without content has vertical orientation" );
1526 pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFrameFormat) );
1531 SwTableBox *HTMLTable::NewTableBox( const SwStartNode *pStNd,
1532 SwTableLine *pUpper ) const
1534 SwTableBox *pBox;
1536 if (m_xBox1 && m_xBox1->GetSttNd() == pStNd)
1538 // If the StartNode is the StartNode of the initially created box, we take that box
1539 pBox = const_cast<HTMLTable*>(this)->m_xBox1.release();
1540 pBox->SetUpper(pUpper);
1542 else
1543 pBox = new SwTableBox( m_pBoxFormat, *pStNd, pUpper );
1545 return pBox;
1548 static void ResetLineFrameFormatAttrs( SwFrameFormat *pFrameFormat )
1550 pFrameFormat->ResetFormatAttr( RES_FRM_SIZE );
1551 pFrameFormat->ResetFormatAttr( RES_BACKGROUND );
1552 OSL_ENSURE( SfxItemState::SET!=pFrameFormat->GetAttrSet().GetItemState(
1553 RES_VERT_ORIENT, false ),
1554 "Cell has vertical orientation" );
1557 // !!! could be simplified
1558 SwTableLine *HTMLTable::MakeTableLine( SwTableBox *pUpper,
1559 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
1560 sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
1562 SwTableLine *pLine;
1563 if (!pUpper && 0 == nTopRow)
1564 pLine = (m_pSwTable->GetTabLines())[0];
1565 else
1566 pLine = new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight
1567 : m_pLineFormat,
1568 0, pUpper );
1570 const HTMLTableRow& rTopRow = m_aRows[nTopRow];
1571 sal_uInt16 nRowHeight = rTopRow.GetHeight();
1572 const SvxBrushItem *pBGBrushItem = nullptr;
1573 if (nTopRow > 0 || nBottomRow < m_nRows)
1575 // It doesn't make sense to set a color on a line,
1576 // if it's the outermost and simultaneously sole line of a table in a table
1577 pBGBrushItem = rTopRow.GetBGBrush().get();
1579 if( nTopRow==nBottomRow-1 && (nRowHeight || pBGBrushItem) )
1581 SwTableLineFormat *pFrameFormat = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1582 ResetLineFrameFormatAttrs( pFrameFormat );
1584 if( nRowHeight )
1586 // set table height. Since it's a minimum height it can be calculated like in Netscape,
1587 // so without considering the actual border width
1588 nRowHeight += GetTopCellSpace( nTopRow ) +
1589 GetBottomCellSpace( nTopRow, 1 );
1591 pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, nRowHeight ) );
1594 if( pBGBrushItem )
1596 pFrameFormat->SetFormatAttr( *pBGBrushItem );
1600 else if( !m_pLineFrameFormatNoHeight )
1602 // else, we'll have to remove the height from the attribute and remember the format
1603 m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1605 ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
1608 SwTableBoxes& rBoxes = pLine->GetTabBoxes();
1610 sal_uInt16 nStartCol = nLeftCol;
1611 while( nStartCol<nRightCol )
1613 sal_uInt16 nCol = nStartCol;
1614 sal_uInt16 nSplitCol = nRightCol;
1615 bool bSplitted = false;
1616 while( !bSplitted )
1618 OSL_ENSURE( nCol < nRightCol, "Gone too far" );
1620 HTMLTableCell& rCell = GetCell(nTopRow,nCol);
1621 const bool bSplit = 1 == rCell.GetColSpan();
1623 OSL_ENSURE((nCol != nRightCol-1) || bSplit, "Split-Flag wrong");
1624 if( bSplit )
1626 SwTableBox* pBox = nullptr;
1627 HTMLTableCell& rCell2 = GetCell(nTopRow, nStartCol);
1628 if (rCell2.GetColSpan() == (nCol+1-nStartCol))
1630 // The HTML tables represent a box. So we need to split behind that box
1631 nSplitCol = nCol + 1;
1633 sal_Int32 nBoxRowSpan = rCell2.GetRowSpan();
1634 if (!rCell2.GetContents() || rCell2.IsCovered())
1636 if (rCell2.IsCovered())
1637 nBoxRowSpan = -1 * nBoxRowSpan;
1639 const SwStartNode* pPrevStartNd =
1640 GetPrevBoxStartNode( nTopRow, nStartCol );
1641 auto xCnts = std::make_shared<HTMLTableCnts>(
1642 m_pParser->InsertTableSection(pPrevStartNd));
1643 const std::shared_ptr<SwHTMLTableLayoutCnts> xCntsLayoutInfo =
1644 xCnts->CreateLayoutInfo();
1646 rCell2.SetContents(xCnts);
1647 SwHTMLTableLayoutCell *pCurrCell = m_xLayoutInfo->GetCell(nTopRow, nStartCol);
1648 pCurrCell->SetContents(xCntsLayoutInfo);
1649 if( nBoxRowSpan < 0 )
1650 pCurrCell->SetRowSpan( 0 );
1652 // check COLSPAN if needed
1653 for( sal_uInt16 j=nStartCol+1; j<nSplitCol; j++ )
1655 GetCell(nTopRow, j).SetContents(xCnts);
1656 m_xLayoutInfo->GetCell(nTopRow, j)
1657 ->SetContents(xCntsLayoutInfo);
1661 pBox = MakeTableBox(pLine, rCell2.GetContents().get(),
1662 nTopRow, nStartCol,
1663 nBottomRow, nSplitCol);
1665 if (1 != nBoxRowSpan && pBox)
1666 pBox->setRowSpan( nBoxRowSpan );
1668 bSplitted = true;
1671 OSL_ENSURE( pBox, "Colspan trouble" );
1673 if( pBox )
1674 rBoxes.push_back( pBox );
1676 nCol++;
1678 nStartCol = nSplitCol;
1681 return pLine;
1684 SwTableBox *HTMLTable::MakeTableBox( SwTableLine *pUpper,
1685 HTMLTableCnts *pCnts,
1686 sal_uInt16 nTopRow, sal_uInt16 nLeftCol,
1687 sal_uInt16 nBottomRow, sal_uInt16 nRightCol )
1689 SwTableBox *pBox;
1690 sal_uInt16 nColSpan = nRightCol - nLeftCol;
1691 sal_uInt16 nRowSpan = nBottomRow - nTopRow;
1693 if( !pCnts->Next() )
1695 // just one content section
1696 if( pCnts->GetStartNode() )
1698 // ... that's not a table
1699 pBox = NewTableBox( pCnts->GetStartNode(), pUpper );
1700 pCnts->SetTableBox( pBox );
1702 else if (HTMLTable* pTable = pCnts->GetTable().get())
1704 pTable->InheritVertBorders( this, nLeftCol,
1705 nRightCol-nLeftCol );
1706 // ... that's a table. We'll build a new box and put the rows of the table
1707 // in the rows of the box
1708 pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
1709 sal_uInt16 nAbs, nRel;
1710 m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel );
1711 sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol, nColSpan );
1712 sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol, nColSpan );
1713 sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan );
1714 pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace, nRSpace,
1715 nInhSpace );
1717 else
1719 return nullptr;
1722 else
1724 // multiple content sections: we'll build a box with rows
1725 pBox = new SwTableBox( m_pBoxFormat, 0, pUpper );
1726 SwTableLines& rLines = pBox->GetTabLines();
1727 bool bFirstPara = true;
1729 while( pCnts )
1731 if( pCnts->GetStartNode() )
1733 // normal paragraphs are gonna be boxes in a row
1734 SwTableLine *pLine =
1735 new SwTableLine( m_pLineFrameFormatNoHeight ? m_pLineFrameFormatNoHeight
1736 : m_pLineFormat, 0, pBox );
1737 if( !m_pLineFrameFormatNoHeight )
1739 // If there's no line format without height yet, we can use that one
1740 m_pLineFrameFormatNoHeight = static_cast<SwTableLineFormat*>(pLine->ClaimFrameFormat());
1742 ResetLineFrameFormatAttrs( m_pLineFrameFormatNoHeight );
1745 SwTableBox* pCntBox = NewTableBox( pCnts->GetStartNode(),
1746 pLine );
1747 pCnts->SetTableBox( pCntBox );
1748 FixFrameFormat( pCntBox, nTopRow, nLeftCol, nRowSpan, nColSpan,
1749 bFirstPara, nullptr==pCnts->Next() );
1750 pLine->GetTabBoxes().push_back( pCntBox );
1752 rLines.push_back( pLine );
1754 else
1756 pCnts->GetTable()->InheritVertBorders( this, nLeftCol,
1757 nRightCol-nLeftCol );
1758 // Tables are entered directly
1759 sal_uInt16 nAbs, nRel;
1760 m_xLayoutInfo->GetAvail( nLeftCol, nColSpan, nAbs, nRel );
1761 sal_uInt16 nLSpace = m_xLayoutInfo->GetLeftCellSpace( nLeftCol,
1762 nColSpan );
1763 sal_uInt16 nRSpace = m_xLayoutInfo->GetRightCellSpace( nLeftCol,
1764 nColSpan );
1765 sal_uInt16 nInhSpace = m_xLayoutInfo->GetInhCellSpace( nLeftCol, nColSpan );
1766 pCnts->GetTable()->MakeTable( pBox, nAbs, nRel, nLSpace,
1767 nRSpace, nInhSpace );
1770 pCnts = pCnts->Next();
1771 bFirstPara = false;
1775 FixFrameFormat( pBox, nTopRow, nLeftCol, nRowSpan, nColSpan );
1777 return pBox;
1780 void HTMLTable::InheritBorders( const HTMLTable *pParent,
1781 sal_uInt16 nRow, sal_uInt16 nCol,
1782 sal_uInt16 nRowSpan,
1783 bool bFirstPara, bool bLastPara )
1785 OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
1786 "Was CloseTable not called?" );
1788 // The child table needs a border, if the surrounding cell has a margin on that side.
1789 // The upper/lower border is only set if the table is the first/last paragraph in that cell
1790 // It can't be determined if a border for that table is needed or possible for the left or right side,
1791 // since that's depending on if filler cells are gonna be added. We'll only collect info for now
1793 if( 0==nRow && pParent->m_bTopBorder && bFirstPara )
1795 m_bTopBorder = true;
1796 m_bFillerTopBorder = true; // fillers get a border too
1797 m_aTopBorderLine = pParent->m_aTopBorderLine;
1799 if (pParent->m_aRows[nRow+nRowSpan-1].GetBottomBorder() && bLastPara)
1801 m_aRows[m_nRows-1].SetBottomBorder(true);
1802 m_bFillerBottomBorder = true; // fillers get a border too
1803 m_aBottomBorderLine =
1804 nRow+nRowSpan==pParent->m_nRows ? pParent->m_aBottomBorderLine
1805 : pParent->m_aBorderLine;
1808 // The child table mustn't get an upper or lower border, if that's already done by the surrounding table
1809 // It can get an upper border if the table is not the first paragraph in that cell
1810 m_bTopAllowed = ( !bFirstPara || (pParent->m_bTopAllowed &&
1811 (0==nRow || !pParent->m_aRows[nRow-1].GetBottomBorder())) );
1813 // The child table has to inherit the color of the cell it's contained in, if it doesn't have one
1814 const SvxBrushItem *pInhBG = pParent->GetCell(nRow, nCol).GetBGBrush().get();
1815 if( !pInhBG && pParent != this &&
1816 pParent->GetCell(nRow,nCol).GetRowSpan() == pParent->m_nRows )
1818 // the whole surrounding table is a table in a table and consists only of a single line
1819 // that's gonna be GC-ed (correctly). That's why the background of that line is copied.
1820 pInhBG = pParent->m_aRows[nRow].GetBGBrush().get();
1821 if( !pInhBG )
1822 pInhBG = pParent->GetBGBrush().get();
1823 if( !pInhBG )
1824 pInhBG = pParent->GetInhBGBrush().get();
1826 if( pInhBG )
1827 m_xInheritedBackgroundBrush.reset(new SvxBrushItem(*pInhBG));
1830 void HTMLTable::InheritVertBorders( const HTMLTable *pParent,
1831 sal_uInt16 nCol, sal_uInt16 nColSpan )
1833 sal_uInt16 nInhLeftBorderWidth = 0;
1834 sal_uInt16 nInhRightBorderWidth = 0;
1836 if( nCol+nColSpan==pParent->m_nCols && pParent->m_bRightBorder )
1838 m_bInheritedRightBorder = true; // just remember for now
1839 m_aInheritedRightBorderLine = pParent->m_aRightBorderLine;
1840 nInhRightBorderWidth =
1841 GetBorderWidth( m_aInheritedRightBorderLine, true ) + MIN_BORDER_DIST;
1844 if (pParent->m_aColumns[nCol].m_bLeftBorder)
1846 m_bInheritedLeftBorder = true; // just remember for now
1847 m_aInheritedLeftBorderLine = 0==nCol ? pParent->m_aLeftBorderLine
1848 : pParent->m_aBorderLine;
1849 nInhLeftBorderWidth =
1850 GetBorderWidth( m_aInheritedLeftBorderLine, true ) + MIN_BORDER_DIST;
1853 if( !m_bInheritedLeftBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) )
1854 nInhLeftBorderWidth = 2 * MIN_BORDER_DIST;
1855 if( !m_bInheritedRightBorder && (m_bFillerTopBorder || m_bFillerBottomBorder) )
1856 nInhRightBorderWidth = 2 * MIN_BORDER_DIST;
1857 m_xLayoutInfo->SetInhBorderWidths( nInhLeftBorderWidth,
1858 nInhRightBorderWidth );
1860 m_bRightAllowed = ( pParent->m_bRightAllowed &&
1861 (nCol+nColSpan==pParent->m_nCols ||
1862 !pParent->m_aColumns[nCol+nColSpan].m_bLeftBorder) );
1865 void HTMLTable::SetBorders()
1867 sal_uInt16 i;
1868 for( i=1; i<m_nCols; i++ )
1869 if( HTMLTableRules::All==m_eRules || HTMLTableRules::Cols==m_eRules ||
1870 ((HTMLTableRules::Rows==m_eRules || HTMLTableRules::Groups==m_eRules) &&
1871 m_aColumns[i-1].IsEndOfGroup()))
1873 m_aColumns[i].m_bLeftBorder = true;
1876 for( i=0; i<m_nRows-1; i++ )
1877 if( HTMLTableRules::All==m_eRules || HTMLTableRules::Rows==m_eRules ||
1878 ((HTMLTableRules::Cols==m_eRules || HTMLTableRules::Groups==m_eRules) &&
1879 m_aRows[i].IsEndOfGroup()))
1881 m_aRows[i].SetBottomBorder(true);
1884 if( m_bTopAllowed && (HTMLTableFrame::Above==m_eFrame || HTMLTableFrame::HSides==m_eFrame ||
1885 HTMLTableFrame::Box==m_eFrame) )
1886 m_bTopBorder = true;
1887 if( HTMLTableFrame::Below==m_eFrame || HTMLTableFrame::HSides==m_eFrame ||
1888 HTMLTableFrame::Box==m_eFrame )
1890 m_aRows[m_nRows-1].SetBottomBorder(true);
1892 if( HTMLTableFrame::RHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame ||
1893 HTMLTableFrame::Box==m_eFrame )
1894 m_bRightBorder = true;
1895 if( HTMLTableFrame::LHS==m_eFrame || HTMLTableFrame::VSides==m_eFrame || HTMLTableFrame::Box==m_eFrame )
1897 m_aColumns[0].m_bLeftBorder = true;
1900 for( i=0; i<m_nRows; i++ )
1902 HTMLTableRow& rRow = m_aRows[i];
1903 for (sal_uInt16 j=0; j<m_nCols; ++j)
1905 HTMLTableCell& rCell = rRow.GetCell(j);
1906 if (rCell.GetContents())
1908 HTMLTableCnts *pCnts = rCell.GetContents().get();
1909 bool bFirstPara = true;
1910 while( pCnts )
1912 HTMLTable *pTable = pCnts->GetTable().get();
1913 if( pTable && !pTable->BordersSet() )
1915 pTable->InheritBorders(this, i, j,
1916 rCell.GetRowSpan(),
1917 bFirstPara,
1918 nullptr==pCnts->Next());
1919 pTable->SetBorders();
1921 bFirstPara = false;
1922 pCnts = pCnts->Next();
1928 m_bBordersSet = true;
1931 sal_uInt16 HTMLTable::GetBorderWidth( const SvxBorderLine& rBLine,
1932 bool bWithDistance ) const
1934 sal_uInt16 nBorderWidth = rBLine.GetWidth();
1935 if( bWithDistance )
1937 if( m_nCellPadding )
1938 nBorderWidth = nBorderWidth + m_nCellPadding;
1939 else if( nBorderWidth )
1940 nBorderWidth = nBorderWidth + MIN_BORDER_DIST;
1943 return nBorderWidth;
1946 const HTMLTableCell& HTMLTable::GetCell(sal_uInt16 nRow, sal_uInt16 nCell) const
1948 OSL_ENSURE(nRow < m_aRows.size(), "invalid row index in HTML table");
1949 return m_aRows[nRow].GetCell(nCell);
1952 SvxAdjust HTMLTable::GetInheritedAdjust() const
1954 SvxAdjust eAdjust = (m_nCurrentColumn<m_nCols ? m_aColumns[m_nCurrentColumn].GetAdjust()
1955 : SvxAdjust::End );
1956 if( SvxAdjust::End==eAdjust )
1957 eAdjust = m_aRows[m_nCurrentRow].GetAdjust();
1959 return eAdjust;
1962 sal_Int16 HTMLTable::GetInheritedVertOri() const
1964 // text::VertOrientation::TOP is default!
1965 sal_Int16 eVOri = m_aRows[m_nCurrentRow].GetVertOri();
1966 if( text::VertOrientation::TOP==eVOri && m_nCurrentColumn<m_nCols )
1967 eVOri = m_aColumns[m_nCurrentColumn].GetVertOri();
1968 if( text::VertOrientation::TOP==eVOri )
1969 eVOri = m_eVertOrientation;
1971 OSL_ENSURE( m_eVertOrientation != text::VertOrientation::TOP, "text::VertOrientation::TOP is not allowed!" );
1972 return eVOri;
1975 void HTMLTable::InsertCell( std::shared_ptr<HTMLTableCnts> const& rCnts,
1976 sal_uInt16 nRowSpan, sal_uInt16 nColSpan,
1977 sal_uInt16 nCellWidth, bool bRelWidth, sal_uInt16 nCellHeight,
1978 sal_Int16 eVertOrient, std::shared_ptr<SvxBrushItem> const& rBGBrushItem,
1979 std::shared_ptr<SvxBoxItem> const& rBoxItem,
1980 bool bHasNumFormat, sal_uInt32 nNumFormat,
1981 bool bHasValue, double nValue, bool bNoWrap )
1983 if( !nRowSpan || static_cast<sal_uInt32>(m_nCurrentRow) + nRowSpan > USHRT_MAX )
1984 nRowSpan = 1;
1986 if( !nColSpan || static_cast<sal_uInt32>(m_nCurrentColumn) + nColSpan > USHRT_MAX )
1987 nColSpan = 1;
1989 sal_uInt16 nColsReq = m_nCurrentColumn + nColSpan;
1990 sal_uInt16 nRowsReq = m_nCurrentRow + nRowSpan;
1991 sal_uInt16 i, j;
1993 // if we need more columns than we currently have, we need to add cells for all rows
1994 if( m_nCols < nColsReq )
1996 m_aColumns.resize(nColsReq);
1997 for( i=0; i<m_nRows; i++ )
1998 m_aRows[i].Expand( nColsReq, i<m_nCurrentRow );
1999 m_nCols = nColsReq;
2000 OSL_ENSURE(m_aColumns.size() == m_nCols,
2001 "wrong number of columns after expanding");
2003 if( nColsReq > m_nFilledColumns )
2004 m_nFilledColumns = nColsReq;
2006 // if we need more rows than we currently have, we need to add cells
2007 if( m_nRows < nRowsReq )
2009 for( i=m_nRows; i<nRowsReq; i++ )
2010 m_aRows.emplace_back(m_nCols);
2011 m_nRows = nRowsReq;
2012 OSL_ENSURE(m_nRows == m_aRows.size(), "wrong number of rows in Insert");
2015 // Check if we have an overlap and could remove that
2016 sal_uInt16 nSpanedCols = 0;
2017 if( m_nCurrentRow>0 )
2019 HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
2020 for( i=m_nCurrentColumn; i<nColsReq; i++ )
2022 HTMLTableCell& rCell = rCurRow.GetCell(i);
2023 if (rCell.GetContents())
2025 // A cell from a row further above overlaps this one.
2026 // Content and colors are coming from that cell and can be overwritten
2027 // or deleted (content) or copied (color) by ProtectRowSpan
2028 nSpanedCols = i + rCell.GetColSpan();
2029 FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() );
2030 if (rCell.GetRowSpan() > nRowSpan)
2031 ProtectRowSpan( nRowsReq, i,
2032 rCell.GetRowSpan()-nRowSpan );
2035 for( i=nColsReq; i<nSpanedCols; i++ )
2037 // These contents are anchored in the row above in any case
2038 HTMLTableCell& rCell = rCurRow.GetCell(i);
2039 FixRowSpan( m_nCurrentRow-1, i, rCell.GetContents().get() );
2040 ProtectRowSpan( m_nCurrentRow, i, rCell.GetRowSpan() );
2044 // Fill the cells
2045 for( i=nColSpan; i>0; i-- )
2047 for( j=nRowSpan; j>0; j-- )
2049 const bool bCovered = i != nColSpan || j != nRowSpan;
2050 GetCell( nRowsReq-j, nColsReq-i )
2051 .Set( rCnts, j, i, eVertOrient, rBGBrushItem, rBoxItem,
2052 bHasNumFormat, nNumFormat, bHasValue, nValue, bNoWrap, bCovered );
2056 Size aTwipSz( bRelWidth ? 0 : nCellWidth, nCellHeight );
2057 if( aTwipSz.Width() || aTwipSz.Height() )
2059 aTwipSz = o3tl::convert(aTwipSz, o3tl::Length::px, o3tl::Length::twip);
2062 // Only set width on the first cell!
2063 if( nCellWidth )
2065 sal_uInt16 nTmp = bRelWidth ? nCellWidth : o3tl::narrowing<sal_uInt16>(aTwipSz.Width());
2066 GetCell( m_nCurrentRow, m_nCurrentColumn ).SetWidth( nTmp, bRelWidth );
2069 // Remember height
2070 if( nCellHeight && 1==nRowSpan )
2072 m_aRows[m_nCurrentRow].SetHeight(o3tl::narrowing<sal_uInt16>(aTwipSz.Height()));
2075 // Set the column counter behind the new cells
2076 m_nCurrentColumn = nColsReq;
2077 if( nSpanedCols > m_nCurrentColumn )
2078 m_nCurrentColumn = nSpanedCols;
2080 // and search for the next free cell
2081 while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
2082 m_nCurrentColumn++;
2085 inline void HTMLTable::CloseSection( bool bHead )
2087 // Close the preceding sections if there's already a row
2088 OSL_ENSURE( m_nCurrentRow<=m_nRows, "invalid current row" );
2089 if( m_nCurrentRow>0 && m_nCurrentRow<=m_nRows )
2090 m_aRows[m_nCurrentRow-1].SetEndOfGroup();
2091 if( bHead )
2092 m_nHeadlineRepeat = m_nCurrentRow;
2095 void HTMLTable::OpenRow(SvxAdjust eAdjust, sal_Int16 eVertOrient,
2096 std::unique_ptr<SvxBrushItem>& rBGBrushItem)
2098 sal_uInt16 nRowsReq = m_nCurrentRow+1;
2100 // create the next row if it's not there already
2101 if( m_nRows<nRowsReq )
2103 for( sal_uInt16 i=m_nRows; i<nRowsReq; i++ )
2104 m_aRows.emplace_back(m_nCols);
2105 m_nRows = nRowsReq;
2106 OSL_ENSURE( m_nRows == m_aRows.size(),
2107 "Row number in OpenRow is wrong" );
2110 HTMLTableRow& rCurRow = m_aRows[m_nCurrentRow];
2111 rCurRow.SetAdjust(eAdjust);
2112 rCurRow.SetVertOri(eVertOrient);
2113 if (rBGBrushItem)
2114 m_aRows[m_nCurrentRow].SetBGBrush(rBGBrushItem);
2116 // reset the column counter
2117 m_nCurrentColumn=0;
2119 // and search for the next free cell
2120 while( m_nCurrentColumn<m_nCols && GetCell(m_nCurrentRow,m_nCurrentColumn).IsUsed() )
2121 m_nCurrentColumn++;
2124 void HTMLTable::CloseRow( bool bEmpty )
2126 OSL_ENSURE( m_nCurrentRow<m_nRows, "current row after table end" );
2128 // empty cells just get a slightly thicker lower border!
2129 if( bEmpty )
2131 if( m_nCurrentRow > 0 )
2132 m_aRows[m_nCurrentRow-1].IncEmptyRows();
2133 return;
2136 HTMLTableRow& rRow = m_aRows[m_nCurrentRow];
2138 // modify the COLSPAN of all empty cells at the row end in a way, that they're forming a single cell
2139 // that can be done here (and not earlier) since there's no more cells in that row
2140 sal_uInt16 i=m_nCols;
2141 while( i )
2143 HTMLTableCell& rCell = rRow.GetCell(--i);
2144 if (!rCell.GetContents())
2146 sal_uInt16 nColSpan = m_nCols-i;
2147 if( nColSpan > 1 )
2148 rCell.SetColSpan(nColSpan);
2150 else
2151 break;
2154 m_nCurrentRow++;
2157 inline void HTMLTable::CloseColGroup( sal_uInt16 nSpan, sal_uInt16 _nWidth,
2158 bool bRelWidth, SvxAdjust eAdjust,
2159 sal_Int16 eVertOrient )
2161 if( nSpan )
2162 InsertCol( nSpan, _nWidth, bRelWidth, eAdjust, eVertOrient );
2164 OSL_ENSURE( m_nCurrentColumn<=m_nCols, "invalid column" );
2165 if( m_nCurrentColumn>0 && m_nCurrentColumn<=m_nCols )
2166 m_aColumns[m_nCurrentColumn-1].SetEndOfGroup();
2169 void HTMLTable::InsertCol( sal_uInt16 nSpan, sal_uInt16 nColWidth, bool bRelWidth,
2170 SvxAdjust eAdjust, sal_Int16 eVertOrient )
2172 // #i35143# - no columns, if rows already exist.
2173 if ( m_nRows > 0 )
2174 return;
2176 sal_uInt16 i;
2178 if( !nSpan )
2179 nSpan = 1;
2181 sal_uInt16 nColsReq = m_nCurrentColumn + nSpan;
2183 if( m_nCols < nColsReq )
2185 m_aColumns.resize(nColsReq);
2186 m_nCols = nColsReq;
2189 sal_uInt16 nTwipWidth(bRelWidth ? 0 : o3tl::convert(nColWidth, o3tl::Length::px, o3tl::Length::twip));
2191 for( i=m_nCurrentColumn; i<nColsReq; i++ )
2193 HTMLTableColumn& rCol = m_aColumns[i];
2194 sal_uInt16 nTmp = bRelWidth ? nColWidth : o3tl::narrowing<sal_uInt16>(nTwipWidth);
2195 rCol.SetWidth( nTmp, bRelWidth );
2196 rCol.SetAdjust( eAdjust );
2197 rCol.SetVertOri( eVertOrient );
2200 m_bColSpec = true;
2202 m_nCurrentColumn = nColsReq;
2205 void HTMLTable::CloseTable()
2207 sal_uInt16 i;
2209 // The number of table rows is only dependent on the <TR> elements (i.e. nCurRow).
2210 // Rows that are spanned via ROWSPAN behind nCurRow need to be deleted
2211 // and we need to adjust the ROWSPAN in the rows above
2212 if( m_nRows>m_nCurrentRow )
2214 HTMLTableRow& rPrevRow = m_aRows[m_nCurrentRow-1];
2215 for( i=0; i<m_nCols; i++ )
2217 HTMLTableCell& rCell = rPrevRow.GetCell(i);
2218 if (rCell.GetRowSpan() > 1)
2220 FixRowSpan(m_nCurrentRow-1, i, rCell.GetContents().get());
2221 ProtectRowSpan(m_nCurrentRow, i, m_aRows[m_nCurrentRow].GetCell(i).GetRowSpan());
2224 for( i=m_nRows-1; i>=m_nCurrentRow; i-- )
2225 m_aRows.erase(m_aRows.begin() + i);
2226 m_nRows = m_nCurrentRow;
2229 // if the table has no column, we need to add one
2230 if( 0==m_nCols )
2232 m_aColumns.resize(1);
2233 for( i=0; i<m_nRows; i++ )
2234 m_aRows[i].Expand(1);
2235 m_nCols = 1;
2236 m_nFilledColumns = 1;
2239 // if the table has no row, we need to add one
2240 if( 0==m_nRows )
2242 m_aRows.emplace_back(m_nCols);
2243 m_nRows = 1;
2244 m_nCurrentRow = 1;
2247 if( m_nFilledColumns < m_nCols )
2249 m_aColumns.erase(m_aColumns.begin() + m_nFilledColumns, m_aColumns.begin() + m_nCols);
2250 for( i=0; i<m_nRows; i++ )
2251 m_aRows[i].Shrink( m_nFilledColumns );
2252 m_nCols = m_nFilledColumns;
2256 void HTMLTable::MakeTable_( SwTableBox *pBox )
2258 SwTableLines& rLines = (pBox ? pBox->GetTabLines()
2259 : const_cast<SwTable *>(m_pSwTable)->GetTabLines() );
2261 for( sal_uInt16 i=0; i<m_nRows; i++ )
2263 SwTableLine *pLine = MakeTableLine( pBox, i, 0, i+1, m_nCols );
2264 if( pBox || i > 0 )
2265 rLines.push_back( pLine );
2269 /* How are tables aligned?
2271 first row: without paragraph indents
2272 second row: with paragraph indents
2274 ALIGN= LEFT RIGHT CENTER -
2275 -------------------------------------------------------------------------
2276 xxx for tables with WIDTH=nn% the percentage value is important:
2277 xxx nn = 100 text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL text::HoriOrientation::FULL %
2278 xxx text::HoriOrientation::NONE text::HoriOrientation::NONE text::HoriOrientation::NONE % text::HoriOrientation::NONE %
2279 xxx nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
2280 xxx frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE %
2282 for tables with WIDTH=nn% the percentage value is important:
2283 nn = 100 text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
2284 text::HoriOrientation::LEFT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER % text::HoriOrientation::LEFT_AND %
2285 nn < 100 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::LEFT %
2286 frame F frame F text::HoriOrientation::CENTER % text::HoriOrientation::NONE %
2288 otherwise the calculated width w
2289 w = avail* text::HoriOrientation::LEFT text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT
2290 HORI_LEDT_AND text::HoriOrientation::RIGHT text::HoriOrientation::CENTER text::HoriOrientation::LEFT_AND
2291 w < avail frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::LEFT
2292 frame L frame L text::HoriOrientation::CENTER text::HoriOrientation::NONE
2294 xxx *) if for the table no size was specified, always
2295 xxx text::HoriOrientation::FULL is taken
2299 void HTMLTable::MakeTable( SwTableBox *pBox, sal_uInt16 nAbsAvail,
2300 sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
2301 sal_uInt16 nAbsRightSpace, sal_uInt16 nInhAbsSpace )
2303 OSL_ENSURE( m_nRows>0 && m_nCols>0 && m_nCurrentRow==m_nRows,
2304 "Was CloseTable not called?" );
2306 OSL_ENSURE(m_xLayoutInfo == nullptr, "Table already has layout info");
2308 // Calculate borders of the table and all contained tables
2309 SetBorders();
2311 // Step 1: needed layout structures are created (including tables in tables)
2312 CreateLayoutInfo();
2314 if (!utl::ConfigManager::IsFuzzing()) // skip slow path for fuzzing
2316 // Step 2: the minimal and maximal column width is calculated
2317 // (including tables in tables). Since we don't have boxes yet,
2318 // we'll work on the start nodes
2319 m_xLayoutInfo->AutoLayoutPass1();
2321 // Step 3: the actual column widths of this table are calculated (not tables in tables)
2322 // We need this now to decide if we need filler cells
2323 // (Pass1 was needed because of this as well)
2324 m_xLayoutInfo->AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace,
2325 nAbsRightSpace, nInhAbsSpace );
2328 // Set adjustment for the top table
2329 sal_Int16 eHoriOri;
2330 if (m_bForceFrame)
2332 // The table should go in a text frame and it's narrower than the
2333 // available space and not 100% wide. So it gets a border
2334 eHoriOri = m_bPercentWidth ? text::HoriOrientation::FULL : text::HoriOrientation::LEFT;
2336 else switch (m_eTableAdjust)
2338 // The table either fits the page but shouldn't get a text frame,
2339 // or it's wider than the page so it doesn't need a text frame
2341 case SvxAdjust::Right:
2342 // Don't be considerate of the right margin in right-adjusted tables
2343 eHoriOri = text::HoriOrientation::RIGHT;
2344 break;
2345 case SvxAdjust::Center:
2346 // Centred tables are not considerate of margins
2347 eHoriOri = text::HoriOrientation::CENTER;
2348 break;
2349 case SvxAdjust::Left:
2350 default:
2351 // left-adjusted tables are only considerate of the left margin
2352 eHoriOri = m_nLeftMargin ? text::HoriOrientation::LEFT_AND_WIDTH : text::HoriOrientation::LEFT;
2353 break;
2356 if (!m_pSwTable)
2358 SAL_WARN("sw.html", "no table");
2359 return;
2362 // get the table format and adapt it
2363 SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
2364 pFrameFormat->SetFormatAttr( SwFormatHoriOrient(0, eHoriOri) );
2365 if (text::HoriOrientation::LEFT_AND_WIDTH == eHoriOri)
2367 OSL_ENSURE( m_nLeftMargin || m_nRightMargin,
2368 "There are still leftovers from relative margins" );
2370 // The right margin will be ignored anyway.
2371 SvxLRSpaceItem aLRItem( m_pSwTable->GetFrameFormat()->GetLRSpace() );
2372 aLRItem.SetLeft( m_nLeftMargin );
2373 aLRItem.SetRight( m_nRightMargin );
2374 pFrameFormat->SetFormatAttr( aLRItem );
2377 if (m_bPercentWidth && text::HoriOrientation::FULL != eHoriOri)
2379 pFrameFormat->LockModify();
2380 SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
2381 aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidth) );
2382 pFrameFormat->SetFormatAttr( aFrameSize );
2383 pFrameFormat->UnlockModify();
2386 // get the default line and box format
2387 // remember the first box and unlist it from the first row
2388 SwTableLine *pLine1 = (m_pSwTable->GetTabLines())[0];
2389 m_xBox1.reset((pLine1->GetTabBoxes())[0]);
2390 pLine1->GetTabBoxes().erase(pLine1->GetTabBoxes().begin());
2392 m_pLineFormat = static_cast<SwTableLineFormat*>(pLine1->GetFrameFormat());
2393 m_pBoxFormat = static_cast<SwTableBoxFormat*>(m_xBox1->GetFrameFormat());
2395 MakeTable_( pBox );
2397 // Finally, we'll do a garbage collection for the top level table
2399 if( 1==m_nRows && m_nHeight && 1==m_pSwTable->GetTabLines().size() )
2401 // Set height of a one-row table as the minimum width of the row
2402 // Was originally a fixed height, but that made problems
2403 // and is not Netscape 4.0 compliant
2404 m_nHeight = SwHTMLParser::ToTwips( m_nHeight );
2405 if( m_nHeight < MINLAY )
2406 m_nHeight = MINLAY;
2408 (m_pSwTable->GetTabLines())[0]->ClaimFrameFormat();
2409 (m_pSwTable->GetTabLines())[0]->GetFrameFormat()
2410 ->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Minimum, 0, m_nHeight ) );
2413 if( GetBGBrush() )
2414 m_pSwTable->GetFrameFormat()->SetFormatAttr( *GetBGBrush() );
2416 const_cast<SwTable *>(m_pSwTable)->SetRowsToRepeat( static_cast< sal_uInt16 >(m_nHeadlineRepeat) );
2417 const_cast<SwTable *>(m_pSwTable)->GCLines();
2419 bool bIsInFlyFrame = m_pContext && m_pContext->GetFrameFormat();
2420 if( bIsInFlyFrame && !m_nWidth )
2422 SvxAdjust eAdjust = GetTableAdjust(false);
2423 if (eAdjust != SvxAdjust::Left &&
2424 eAdjust != SvxAdjust::Right)
2426 // If a table with a width attribute isn't flowed around left or right
2427 // we'll stack it with a border of 100% width, so its size will
2428 // be adapted. That text frame mustn't be modified
2429 OSL_ENSURE( HasToFly(), "Why is the table in a frame?" );
2430 sal_uInt32 nMin = m_xLayoutInfo->GetMin();
2431 if( nMin > USHRT_MAX )
2432 nMin = USHRT_MAX;
2433 SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, static_cast<SwTwips>(nMin), MINLAY );
2434 aFlyFrameSize.SetWidthPercent( 100 );
2435 m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize );
2436 bIsInFlyFrame = false;
2438 else
2440 // left or right adjusted table without width mustn't be adjusted in width
2441 // as they would only shrink but never grow
2442 m_xLayoutInfo->SetMustNotRecalc( true );
2443 if( m_pContext->GetFrameFormat()->GetAnchor().GetAnchorNode()
2444 ->FindTableNode() )
2446 sal_uInt32 nMax = m_xLayoutInfo->GetMax();
2447 if( nMax > USHRT_MAX )
2448 nMax = USHRT_MAX;
2449 SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, static_cast<SwTwips>(nMax), MINLAY );
2450 m_pContext->GetFrameFormat()->SetFormatAttr( aFlyFrameSize );
2451 bIsInFlyFrame = false;
2453 else
2455 m_xLayoutInfo->SetMustNotResize( true );
2459 m_xLayoutInfo->SetMayBeInFlyFrame( bIsInFlyFrame );
2461 // Only tables with relative width or without width should be modified
2462 m_xLayoutInfo->SetMustResize( m_bPercentWidth || !m_nWidth );
2464 if (!pLine1->GetTabBoxes().empty())
2465 m_xLayoutInfo->SetWidths();
2466 else
2467 SAL_WARN("sw.html", "no table box");
2469 const_cast<SwTable *>(m_pSwTable)->SetHTMLTableLayout(m_xLayoutInfo);
2471 if( !m_xResizeDrawObjects )
2472 return;
2474 sal_uInt16 nCount = m_xResizeDrawObjects->size();
2475 for( sal_uInt16 i=0; i<nCount; i++ )
2477 SdrObject *pObj = (*m_xResizeDrawObjects)[i];
2478 sal_uInt16 nRow = (*m_xDrawObjectPercentWidths)[3*i];
2479 sal_uInt16 nCol = (*m_xDrawObjectPercentWidths)[3*i+1];
2480 sal_uInt8 nPercentWidth = static_cast<sal_uInt8>((*m_xDrawObjectPercentWidths)[3*i+2]);
2482 SwHTMLTableLayoutCell *pLayoutCell =
2483 m_xLayoutInfo->GetCell( nRow, nCol );
2484 sal_uInt16 nColSpan = pLayoutCell->GetColSpan();
2486 sal_uInt16 nWidth2, nDummy;
2487 m_xLayoutInfo->GetAvail( nCol, nColSpan, nWidth2, nDummy );
2488 nWidth2 = static_cast< sal_uInt16 >((static_cast<tools::Long>(m_nWidth) * nPercentWidth) / 100);
2490 SwHTMLParser::ResizeDrawObject( pObj, nWidth2 );
2495 void HTMLTable::SetTable( const SwStartNode *pStNd, std::unique_ptr<HTMLTableContext> pCntxt,
2496 sal_uInt16 nLeft, sal_uInt16 nRight,
2497 const SwTable *pSwTab, bool bFrcFrame )
2499 m_pPrevStartNode = pStNd;
2500 m_pSwTable = pSwTab;
2501 m_pContext = std::move(pCntxt);
2503 m_nLeftMargin = nLeft;
2504 m_nRightMargin = nRight;
2506 m_bForceFrame = bFrcFrame;
2509 void HTMLTable::RegisterDrawObject( SdrObject *pObj, sal_uInt8 nPercentWidth )
2511 if( !m_xResizeDrawObjects )
2512 m_xResizeDrawObjects.emplace();
2513 m_xResizeDrawObjects->push_back( pObj );
2514 pObj->AddObjectUser(*this);
2516 if( !m_xDrawObjectPercentWidths )
2517 m_xDrawObjectPercentWidths.emplace();
2518 m_xDrawObjectPercentWidths->push_back( m_nCurrentRow );
2519 m_xDrawObjectPercentWidths->push_back( m_nCurrentColumn );
2520 m_xDrawObjectPercentWidths->push_back( o3tl::narrowing<sal_uInt16>(nPercentWidth) );
2523 void HTMLTable::MakeParentContents()
2525 if( !GetContext() && !HasParentSection() )
2527 SetParentContents(
2528 m_pParser->InsertTableContents( m_bIsParentHead ) );
2530 SetHasParentSection( true );
2534 void HTMLTableContext::SavePREListingXMP( SwHTMLParser& rParser )
2536 m_bRestartPRE = rParser.IsReadPRE();
2537 m_bRestartXMP = rParser.IsReadXMP();
2538 m_bRestartListing = rParser.IsReadListing();
2539 rParser.FinishPREListingXMP();
2542 void HTMLTableContext::RestorePREListingXMP( SwHTMLParser& rParser )
2544 rParser.FinishPREListingXMP();
2546 if( m_bRestartPRE )
2547 rParser.StartPRE();
2549 if( m_bRestartXMP )
2550 rParser.StartXMP();
2552 if( m_bRestartListing )
2553 rParser.StartListing();
2556 const SwStartNode *SwHTMLParser::InsertTableSection
2557 ( const SwStartNode *pPrevStNd )
2559 OSL_ENSURE( pPrevStNd, "Start-Node is NULL" );
2561 m_pCSS1Parser->SetTDTagStyles();
2562 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TABLE );
2564 const SwStartNode *pStNd;
2565 if (m_xTable->m_bFirstCell )
2567 SwNode *const pNd = & m_pPam->GetPoint()->GetNode();
2568 pNd->GetTextNode()->ChgFormatColl( pColl );
2569 pStNd = pNd->FindTableBoxStartNode();
2570 m_xTable->m_bFirstCell = false;
2572 else if (pPrevStNd)
2574 const SwNode* pNd;
2575 if( pPrevStNd->IsTableNode() )
2576 pNd = pPrevStNd;
2577 else
2578 pNd = pPrevStNd->EndOfSectionNode();
2579 SwNodeIndex nIdx( *pNd, 1 );
2580 pStNd = m_xDoc->GetNodes().MakeTextSection( nIdx.GetNode(), SwTableBoxStartNode,
2581 pColl );
2582 m_xTable->IncBoxCount();
2584 else
2586 eState = SvParserState::Error;
2587 return nullptr;
2590 //Added defaults to CJK and CTL
2591 SwContentNode *pCNd = m_xDoc->GetNodes()[pStNd->GetIndex()+1] ->GetContentNode();
2592 SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
2593 pCNd->SetAttr( aFontHeight );
2594 SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
2595 pCNd->SetAttr( aFontHeightCJK );
2596 SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
2597 pCNd->SetAttr( aFontHeightCTL );
2599 return pStNd;
2602 const SwStartNode *SwHTMLParser::InsertTableSection( sal_uInt16 nPoolId )
2604 switch( nPoolId )
2606 case RES_POOLCOLL_TABLE_HDLN:
2607 m_pCSS1Parser->SetTHTagStyles();
2608 break;
2609 case RES_POOLCOLL_TABLE:
2610 m_pCSS1Parser->SetTDTagStyles();
2611 break;
2614 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( nPoolId );
2616 SwNode *const pNd = & m_pPam->GetPoint()->GetNode();
2617 const SwStartNode *pStNd;
2618 if (m_xTable->m_bFirstCell)
2620 SwTextNode* pTextNd = pNd->GetTextNode();
2621 if (!pTextNd)
2623 eState = SvParserState::Error;
2624 return nullptr;
2626 pTextNd->ChgFormatColl(pColl);
2627 m_xTable->m_bFirstCell = false;
2628 pStNd = pNd->FindTableBoxStartNode();
2630 else
2632 SwTableNode *pTableNd = pNd->FindTableNode();
2633 if (!pTableNd)
2635 eState = SvParserState::Error;
2636 return nullptr;
2638 if( pTableNd->GetTable().GetHTMLTableLayout() )
2639 { // if there is already a HTMTableLayout, this table is already finished
2640 // and we have to look for the right table in the environment
2641 SwTableNode *pOutTable = pTableNd;
2642 do {
2643 pTableNd = pOutTable;
2644 pOutTable = pOutTable->StartOfSectionNode()->FindTableNode();
2645 } while( pOutTable && pTableNd->GetTable().GetHTMLTableLayout() );
2647 pStNd = m_xDoc->GetNodes().MakeTextSection( *pTableNd->EndOfSectionNode(), SwTableBoxStartNode,
2648 pColl );
2650 m_pPam->GetPoint()->Assign( pStNd->GetIndex() + 1 );
2651 m_xTable->IncBoxCount();
2654 if (!pStNd)
2656 eState = SvParserState::Error;
2659 return pStNd;
2662 SwStartNode *SwHTMLParser::InsertTempTableCaptionSection()
2664 SwTextFormatColl *pColl = m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_TEXT );
2665 SwStartNode *pStNd = m_xDoc->GetNodes().MakeTextSection( m_xDoc->GetNodes().GetEndOfExtras(),
2666 SwNormalStartNode, pColl );
2668 m_pPam->GetPoint()->Assign( pStNd->GetIndex() + 1);
2670 return pStNd;
2673 sal_Int32 SwHTMLParser::StripTrailingLF()
2675 sal_Int32 nStripped = 0;
2677 if (IsReqIF())
2679 // One <br> is exactly one line-break in the ReqIF case.
2680 return nStripped;
2683 const sal_Int32 nLen = m_pPam->GetPoint()->GetContentIndex();
2684 if( nLen )
2686 SwTextNode* pTextNd = m_pPam->GetPoint()->GetNode().GetTextNode();
2687 // careful, when comments aren't ignored!!!
2688 if( pTextNd )
2690 sal_Int32 nPos = nLen;
2691 sal_Int32 nLFCount = 0;
2692 while (nPos && ('\x0a' == pTextNd->GetText()[--nPos]))
2693 nLFCount++;
2695 if( nLFCount )
2697 if( nLFCount > 2 )
2699 // On Netscape, a paragraph end matches 2 LFs
2700 // (1 is just a newline, 2 creates a blank line)
2701 // We already have this space with the lower paragraph gap
2702 // If there's a paragraph after the <BR>, we take the maximum
2703 // of the gap that results from the <BR> and <P>
2704 // That's why we need to delete 2 respectively all if less than 2
2705 nLFCount = 2;
2708 nPos = nLen - nLFCount;
2709 SwContentIndex nIdx( pTextNd, nPos );
2710 pTextNd->EraseText( nIdx, nLFCount );
2711 nStripped = nLFCount;
2716 return nStripped;
2719 SvxBrushItem* SwHTMLParser::CreateBrushItem( const Color *pColor,
2720 const OUString& rImageURL,
2721 const OUString& rStyle,
2722 const OUString& rId,
2723 const OUString& rClass )
2725 SvxBrushItem *pBrushItem = nullptr;
2727 if( !rStyle.isEmpty() || !rId.isEmpty() || !rClass.isEmpty() )
2729 SfxItemSetFixed<RES_BACKGROUND, RES_BACKGROUND> aItemSet( m_xDoc->GetAttrPool() );
2730 SvxCSS1PropertyInfo aPropInfo;
2732 if( !rClass.isEmpty() )
2734 OUString aClass( rClass );
2735 SwCSS1Parser::GetScriptFromClass( aClass );
2736 const SvxCSS1MapEntry *pClass = m_pCSS1Parser->GetClass( aClass );
2737 if( pClass )
2738 aItemSet.Put( pClass->GetItemSet() );
2741 if( !rId.isEmpty() )
2743 const SvxCSS1MapEntry *pId = m_pCSS1Parser->GetId( rId );
2744 if( pId )
2745 aItemSet.Put( pId->GetItemSet() );
2748 m_pCSS1Parser->ParseStyleOption( rStyle, aItemSet, aPropInfo );
2749 if( const SvxBrushItem *pItem = aItemSet.GetItemIfSet( RES_BACKGROUND, false ) )
2751 pBrushItem = new SvxBrushItem( *pItem );
2755 if( !pBrushItem && (pColor || !rImageURL.isEmpty()) )
2757 pBrushItem = new SvxBrushItem(RES_BACKGROUND);
2759 if( pColor )
2760 pBrushItem->SetColor(*pColor);
2762 if( !rImageURL.isEmpty() )
2764 pBrushItem->SetGraphicLink( URIHelper::SmartRel2Abs( INetURLObject(m_sBaseURL), rImageURL, Link<OUString *, bool>(), false) );
2765 pBrushItem->SetGraphicPos( GPOS_TILED );
2769 return pBrushItem;
2772 class SectionSaveStruct : public SwPendingData
2774 sal_uInt16 m_nBaseFontStMinSave, m_nFontStMinSave, m_nFontStHeadStartSave;
2775 sal_uInt16 m_nDefListDeepSave;
2776 size_t m_nContextStMinSave;
2777 size_t m_nContextStAttrMinSave;
2779 public:
2781 std::shared_ptr<HTMLTable> m_xTable;
2783 explicit SectionSaveStruct( SwHTMLParser& rParser );
2785 #if OSL_DEBUG_LEVEL > 0
2786 size_t GetContextStAttrMin() const { return m_nContextStAttrMinSave; }
2787 #endif
2788 void Restore( SwHTMLParser& rParser );
2791 SectionSaveStruct::SectionSaveStruct( SwHTMLParser& rParser ) :
2792 m_nBaseFontStMinSave(rParser.m_nBaseFontStMin),
2793 m_nFontStMinSave(rParser.m_nFontStMin),
2794 m_nFontStHeadStartSave(rParser.m_nFontStHeadStart),
2795 m_nDefListDeepSave(rParser.m_nDefListDeep),
2796 m_nContextStMinSave(rParser.m_nContextStMin),
2797 m_nContextStAttrMinSave(rParser.m_nContextStAttrMin)
2799 // Freeze font stacks
2800 rParser.m_nBaseFontStMin = rParser.m_aBaseFontStack.size();
2802 rParser.m_nFontStMin = rParser.m_aFontStack.size();
2804 // Freeze context stack
2805 rParser.m_nContextStMin = rParser.m_aContexts.size();
2806 rParser.m_nContextStAttrMin = rParser.m_nContextStMin;
2808 // And remember a few counters
2809 rParser.m_nDefListDeep = 0;
2812 void SectionSaveStruct::Restore( SwHTMLParser& rParser )
2814 // Unfreeze font stacks
2815 sal_uInt16 nMin = rParser.m_nBaseFontStMin;
2816 if( rParser.m_aBaseFontStack.size() > nMin )
2817 rParser.m_aBaseFontStack.erase( rParser.m_aBaseFontStack.begin() + nMin,
2818 rParser.m_aBaseFontStack.end() );
2819 rParser.m_nBaseFontStMin = m_nBaseFontStMinSave;
2821 nMin = rParser.m_nFontStMin;
2822 if( rParser.m_aFontStack.size() > nMin )
2823 rParser.m_aFontStack.erase( rParser.m_aFontStack.begin() + nMin,
2824 rParser.m_aFontStack.end() );
2825 rParser.m_nFontStMin = m_nFontStMinSave;
2826 rParser.m_nFontStHeadStart = m_nFontStHeadStartSave;
2828 OSL_ENSURE( rParser.m_aContexts.size() == rParser.m_nContextStMin &&
2829 rParser.m_aContexts.size() == rParser.m_nContextStAttrMin,
2830 "The Context Stack was not cleaned up" );
2831 rParser.m_nContextStMin = m_nContextStMinSave;
2832 rParser.m_nContextStAttrMin = m_nContextStAttrMinSave;
2834 // Reconstruct a few counters
2835 rParser.m_nDefListDeep = m_nDefListDeepSave;
2837 // Reset a few flags
2838 rParser.m_bNoParSpace = false;
2839 rParser.m_nOpenParaToken = HtmlTokenId::NONE;
2841 rParser.m_aParaAttrs.clear();
2844 class CellSaveStruct : public SectionSaveStruct
2846 OUString m_aStyle, m_aId, m_aClass;
2847 OUString m_aBGImage;
2848 Color m_aBGColor;
2849 std::shared_ptr<SvxBoxItem> m_xBoxItem;
2851 std::shared_ptr<HTMLTableCnts> m_xCnts; // List of all contents
2852 HTMLTableCnts* m_pCurrCnts; // current content or 0
2853 std::optional<SwNodeIndex> m_oNoBreakEndNodeIndex; // Paragraph index of a <NOBR>
2855 double m_nValue;
2857 sal_uInt32 m_nNumFormat;
2859 sal_uInt16 m_nRowSpan, m_nColSpan, m_nWidth, m_nHeight;
2860 sal_Int32 m_nNoBreakEndContentPos; // Character index of a <NOBR>
2862 sal_Int16 m_eVertOri;
2864 bool m_bHead : 1;
2865 bool m_bPercentWidth : 1;
2866 bool m_bHasNumFormat : 1;
2867 bool m_bHasValue : 1;
2868 bool m_bBGColor : 1;
2869 bool m_bNoWrap : 1; // NOWRAP option
2870 bool m_bNoBreak : 1; // NOBREAK tag
2872 public:
2874 CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable, bool bHd,
2875 bool bReadOpt );
2877 void AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts );
2878 bool HasFirstContents() const { return bool(m_xCnts); }
2880 void ClearIsInSection() { m_pCurrCnts = nullptr; }
2881 bool IsInSection() const { return m_pCurrCnts!=nullptr; }
2883 void InsertCell( SwHTMLParser& rParser, HTMLTable *pCurTable );
2885 bool IsHeaderCell() const { return m_bHead; }
2887 void StartNoBreak( const SwPosition& rPos );
2888 void EndNoBreak( const SwPosition& rPos );
2889 void CheckNoBreak( const SwPosition& rPos );
2892 CellSaveStruct::CellSaveStruct( SwHTMLParser& rParser, HTMLTable const *pCurTable,
2893 bool bHd, bool bReadOpt ) :
2894 SectionSaveStruct( rParser ),
2895 m_pCurrCnts( nullptr ),
2896 m_nValue( 0.0 ),
2897 m_nNumFormat( 0 ),
2898 m_nRowSpan( 1 ),
2899 m_nColSpan( 1 ),
2900 m_nWidth( 0 ),
2901 m_nHeight( 0 ),
2902 m_nNoBreakEndContentPos( 0 ),
2903 m_eVertOri( pCurTable->GetInheritedVertOri() ),
2904 m_bHead( bHd ),
2905 m_bPercentWidth( false ),
2906 m_bHasNumFormat( false ),
2907 m_bHasValue( false ),
2908 m_bBGColor( false ),
2909 m_bNoWrap( false ),
2910 m_bNoBreak( false )
2912 OUString aNumFormat, aValue, aDir, aLang;
2913 SvxAdjust eAdjust( pCurTable->GetInheritedAdjust() );
2915 if( bReadOpt )
2917 const HTMLOptions& rOptions = rParser.GetOptions();
2918 for (size_t i = rOptions.size(); i; )
2920 const HTMLOption& rOption = rOptions[--i];
2921 switch( rOption.GetToken() )
2923 case HtmlOptionId::ID:
2924 m_aId = rOption.GetString();
2925 break;
2926 case HtmlOptionId::COLSPAN:
2927 m_nColSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
2928 if (m_nColSpan > 256)
2930 SAL_INFO("sw.html", "ignoring huge COLSPAN " << m_nColSpan);
2931 m_nColSpan = 1;
2933 break;
2934 case HtmlOptionId::ROWSPAN:
2935 m_nRowSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
2936 if (m_nRowSpan > 8192 || (m_nRowSpan > 256 && utl::ConfigManager::IsFuzzing()))
2938 SAL_INFO("sw.html", "ignoring huge ROWSPAN " << m_nRowSpan);
2939 m_nRowSpan = 1;
2941 break;
2942 case HtmlOptionId::ALIGN:
2943 eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
2944 break;
2945 case HtmlOptionId::VALIGN:
2946 m_eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, m_eVertOri );
2947 break;
2948 case HtmlOptionId::WIDTH:
2949 m_nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber()); // Just for Netscape
2950 m_bPercentWidth = (rOption.GetString().indexOf('%') != -1);
2951 if( m_bPercentWidth && m_nWidth>100 )
2952 m_nWidth = 100;
2953 break;
2954 case HtmlOptionId::HEIGHT:
2955 m_nHeight = o3tl::narrowing<sal_uInt16>(rOption.GetNumber()); // Just for Netscape
2956 if( rOption.GetString().indexOf('%') != -1)
2957 m_nHeight = 0; // don't consider % attributes
2958 break;
2959 case HtmlOptionId::BGCOLOR:
2960 // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape
2961 // *really* not on other tags
2962 if( !rOption.GetString().isEmpty() )
2964 rOption.GetColor( m_aBGColor );
2965 m_bBGColor = true;
2967 break;
2968 case HtmlOptionId::BACKGROUND:
2969 m_aBGImage = rOption.GetString();
2970 break;
2971 case HtmlOptionId::STYLE:
2972 m_aStyle = rOption.GetString();
2973 break;
2974 case HtmlOptionId::CLASS:
2975 m_aClass = rOption.GetString();
2976 break;
2977 case HtmlOptionId::LANG:
2978 aLang = rOption.GetString();
2979 break;
2980 case HtmlOptionId::DIR:
2981 aDir = rOption.GetString();
2982 break;
2983 case HtmlOptionId::SDNUM:
2984 aNumFormat = rOption.GetString();
2985 m_bHasNumFormat = true;
2986 break;
2987 case HtmlOptionId::SDVAL:
2988 m_bHasValue = true;
2989 aValue = rOption.GetString();
2990 break;
2991 case HtmlOptionId::NOWRAP:
2992 m_bNoWrap = true;
2993 break;
2994 default: break;
2998 if( !m_aId.isEmpty() )
2999 rParser.InsertBookmark( m_aId );
3002 if( m_bHasNumFormat )
3004 LanguageType eLang;
3005 m_nValue = SfxHTMLParser::GetTableDataOptionsValNum(
3006 m_nNumFormat, eLang, aValue, aNumFormat,
3007 *rParser.m_xDoc->GetNumberFormatter() );
3010 // Create a new context but don't anchor the drawing::Alignment attribute there,
3011 // since there's no section yet
3012 HtmlTokenId nToken;
3013 sal_uInt16 nColl;
3014 if( m_bHead )
3016 nToken = HtmlTokenId::TABLEHEADER_ON;
3017 nColl = RES_POOLCOLL_TABLE_HDLN;
3019 else
3021 nToken = HtmlTokenId::TABLEDATA_ON;
3022 nColl = RES_POOLCOLL_TABLE;
3024 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, OUString(), true));
3025 if( SvxAdjust::End != eAdjust )
3026 rParser.InsertAttr(&rParser.m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST),
3027 xCntxt.get());
3029 if( SwHTMLParser::HasStyleOptions( m_aStyle, m_aId, m_aClass, &aLang, &aDir ) )
3031 SfxItemSet aItemSet( rParser.m_xDoc->GetAttrPool(),
3032 rParser.m_pCSS1Parser->GetWhichMap() );
3033 SvxCSS1PropertyInfo aPropInfo;
3035 if( rParser.ParseStyleOptions( m_aStyle, m_aId, m_aClass, aItemSet,
3036 aPropInfo, &aLang, &aDir ) )
3038 if (SvxBoxItem const* pItem = aItemSet.GetItemIfSet(RES_BOX, false))
3039 { // fdo#41796: steal box item to set it in FixFrameFormat later!
3040 m_xBoxItem.reset(pItem->Clone());
3041 aItemSet.ClearItem(RES_BOX);
3043 rParser.InsertAttrs(aItemSet, aPropInfo, xCntxt.get());
3047 rParser.SplitPREListingXMP(xCntxt.get());
3049 rParser.PushContext(xCntxt);
3052 void CellSaveStruct::AddContents( std::unique_ptr<HTMLTableCnts> pNewCnts )
3054 m_pCurrCnts = pNewCnts.get();
3056 if (m_xCnts)
3057 m_xCnts->Add( std::move(pNewCnts) );
3058 else
3059 m_xCnts = std::move(pNewCnts);
3062 void CellSaveStruct::InsertCell( SwHTMLParser& rParser,
3063 HTMLTable *pCurTable )
3065 #if OSL_DEBUG_LEVEL > 0
3066 // The attributes need to have been removed when tidying up the context stack,
3067 // Otherwise something's wrong. Let's check that...
3069 // MIB 8.1.98: When attributes were opened outside of a cell,
3070 // they're still in the attribute table and will only be deleted at the end
3071 // through the CleanContext calls in BuildTable. We don't check that there
3072 // so that we get no assert [violations, by translator]
3073 // We can see this on nContextStAttrMin: the remembered value of nContextStAttrMinSave
3074 // is the value that nContextStAttrMin had at the start of the table. And the
3075 // current value of nContextStAttrMin corresponds to the number of contexts
3076 // we found at the start of the cell. If the values differ, contexts
3077 // were created and we don't check anything.
3079 if( rParser.m_nContextStAttrMin == GetContextStAttrMin() )
3081 HTMLAttr** pTable = reinterpret_cast<HTMLAttr**>(rParser.m_xAttrTab.get());
3083 for( auto nCnt = sizeof( HTMLAttrTable ) / sizeof( HTMLAttr* );
3084 nCnt--; ++pTable )
3086 OSL_ENSURE( !*pTable, "The attribute table isn't empty" );
3089 #endif
3091 // we need to add the cell on the current position
3092 std::shared_ptr<SvxBrushItem> xBrushItem(
3093 rParser.CreateBrushItem(m_bBGColor ? &m_aBGColor : nullptr, m_aBGImage,
3094 m_aStyle, m_aId, m_aClass));
3095 pCurTable->InsertCell( m_xCnts, m_nRowSpan, m_nColSpan, m_nWidth,
3096 m_bPercentWidth, m_nHeight, m_eVertOri, xBrushItem, m_xBoxItem,
3097 m_bHasNumFormat, m_nNumFormat, m_bHasValue, m_nValue,
3098 m_bNoWrap );
3099 Restore( rParser );
3102 void CellSaveStruct::StartNoBreak( const SwPosition& rPos )
3104 if( !m_xCnts ||
3105 (!rPos.GetContentIndex() && m_pCurrCnts == m_xCnts.get() &&
3106 m_xCnts->GetStartNode() &&
3107 m_xCnts->GetStartNode()->GetIndex() + 1 ==
3108 rPos.GetNodeIndex()) )
3110 m_bNoBreak = true;
3114 void CellSaveStruct::EndNoBreak( const SwPosition& rPos )
3116 if( m_bNoBreak )
3118 m_oNoBreakEndNodeIndex.emplace( rPos.GetNode() );
3119 m_nNoBreakEndContentPos = rPos.GetContentIndex();
3120 m_bNoBreak = false;
3124 void CellSaveStruct::CheckNoBreak( const SwPosition& rPos )
3126 if (!(m_xCnts && m_pCurrCnts == m_xCnts.get()))
3127 return;
3129 if( m_bNoBreak )
3131 // <NOBR> wasn't closed
3132 m_xCnts->SetNoBreak();
3134 else if( m_oNoBreakEndNodeIndex &&
3135 m_oNoBreakEndNodeIndex->GetIndex() == rPos.GetNodeIndex() )
3137 if( m_nNoBreakEndContentPos == rPos.GetContentIndex() )
3139 // <NOBR> was closed immediately before the cell end
3140 m_xCnts->SetNoBreak();
3142 else if( m_nNoBreakEndContentPos + 1 == rPos.GetContentIndex() )
3144 SwTextNode const*const pTextNd(rPos.GetNode().GetTextNode());
3145 if( pTextNd )
3147 sal_Unicode const cLast =
3148 pTextNd->GetText()[m_nNoBreakEndContentPos];
3149 if( ' '==cLast || '\x0a'==cLast )
3151 // There's just a blank or a newline between the <NOBR> and the cell end
3152 m_xCnts->SetNoBreak();
3159 std::unique_ptr<HTMLTableCnts> SwHTMLParser::InsertTableContents(
3160 bool bHead )
3162 // create a new section, the PaM is gonna be there
3163 const SwStartNode *pStNd =
3164 InsertTableSection( static_cast< sal_uInt16 >(bHead ? RES_POOLCOLL_TABLE_HDLN
3165 : RES_POOLCOLL_TABLE) );
3167 if( GetNumInfo().GetNumRule() )
3169 // Set the first paragraph to non-enumerated
3170 sal_uInt8 nLvl = GetNumInfo().GetLevel();
3172 SetNodeNum( nLvl );
3175 // Reset attributation start
3176 const SwNode& rSttPara = m_pPam->GetPoint()->GetNode();
3177 sal_Int32 nSttCnt = m_pPam->GetPoint()->GetContentIndex();
3179 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
3180 for (sal_uInt16 nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
3182 HTMLAttr *pAttr = *pHTMLAttributes;
3183 while( pAttr )
3185 OSL_ENSURE( !pAttr->GetPrev(), "Attribute has previous list" );
3186 pAttr->m_nStartPara = rSttPara;
3187 pAttr->m_nEndPara = rSttPara;
3188 pAttr->m_nStartContent = nSttCnt;
3189 pAttr->m_nEndContent = nSttCnt;
3191 pAttr = pAttr->GetNext();
3195 return std::make_unique<HTMLTableCnts>( pStNd );
3198 sal_uInt16 SwHTMLParser::IncGrfsThatResizeTable()
3200 return m_xTable ? m_xTable->IncGrfsThatResize() : 0;
3203 void SwHTMLParser::RegisterDrawObjectToTable( HTMLTable *pCurTable,
3204 SdrObject *pObj, sal_uInt8 nPercentWidth )
3206 pCurTable->RegisterDrawObject( pObj, nPercentWidth );
3209 void SwHTMLParser::BuildTableCell( HTMLTable *pCurTable, bool bReadOptions,
3210 bool bHead )
3212 if( !IsParserWorking() && m_vPendingStack.empty() )
3213 return;
3215 ::comphelper::FlagRestorationGuard g(m_isInTableStructure, false);
3216 std::unique_ptr<CellSaveStruct> xSaveStruct;
3218 HtmlTokenId nToken = HtmlTokenId::NONE;
3219 bool bPending = false;
3220 if( !m_vPendingStack.empty() )
3222 xSaveStruct.reset(static_cast<CellSaveStruct*>(m_vPendingStack.back().pData.release()));
3224 m_vPendingStack.pop_back();
3225 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
3226 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
3228 SaveState( nToken );
3230 else
3232 // <TH> resp. <TD> were already read
3233 if (m_xTable->IsOverflowing())
3235 SaveState( HtmlTokenId::NONE );
3236 return;
3239 if( !pCurTable->GetContext() )
3241 bool bTopTable = m_xTable.get() == pCurTable;
3243 // the table has no content yet, this means the actual table needs
3244 // to be created first
3246 SfxItemSetFixed<
3247 RES_PARATR_SPLIT, RES_PARATR_SPLIT,
3248 RES_PAGEDESC, RES_PAGEDESC,
3249 RES_BREAK, RES_BREAK,
3250 RES_BACKGROUND, RES_BACKGROUND,
3251 RES_KEEP, RES_KEEP,
3252 RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT,
3253 RES_FRAMEDIR, RES_FRAMEDIR
3254 > aItemSet( m_xDoc->GetAttrPool() );
3255 SvxCSS1PropertyInfo aPropInfo;
3257 bool bStyleParsed = ParseStyleOptions( pCurTable->GetStyle(),
3258 pCurTable->GetId(),
3259 pCurTable->GetClass(),
3260 aItemSet, aPropInfo,
3261 nullptr, &pCurTable->GetDirection() );
3262 if( bStyleParsed )
3264 if( const SvxBrushItem* pItem = aItemSet.GetItemIfSet(
3265 RES_BACKGROUND, false ) )
3267 pCurTable->SetBGBrush( *pItem );
3268 aItemSet.ClearItem( RES_BACKGROUND );
3270 if( const SvxFormatSplitItem* pSplitItem = aItemSet.GetItemIfSet(
3271 RES_PARATR_SPLIT, false ) )
3273 aItemSet.Put(
3274 SwFormatLayoutSplit( pSplitItem->GetValue() ) );
3275 aItemSet.ClearItem( RES_PARATR_SPLIT );
3279 sal_uInt16 nLeftSpace = 0;
3280 sal_uInt16 nRightSpace = 0;
3281 short nIndent;
3282 GetMarginsFromContextWithNumberBullet( nLeftSpace, nRightSpace, nIndent );
3284 // save the current position we'll get back to some time
3285 SwPosition *pSavePos = nullptr;
3286 bool bForceFrame = false;
3287 bool bAppended = false;
3288 bool bParentLFStripped = false;
3289 if( bTopTable )
3291 SvxAdjust eTableAdjust = m_xTable->GetTableAdjust(false);
3293 // If the table is left or right adjusted or should be in a text frame,
3294 // it'll get one
3295 bForceFrame = eTableAdjust == SvxAdjust::Left ||
3296 eTableAdjust == SvxAdjust::Right ||
3297 pCurTable->HasToFly();
3299 // The table either shouldn't get in a text frame and isn't in one
3300 // (it gets simulated through cells),
3301 // or there's already content at that position
3302 OSL_ENSURE( !bForceFrame || pCurTable->HasParentSection(),
3303 "table in frame has no parent!" );
3305 bool bAppend = false;
3306 if( bForceFrame )
3308 // If the table gets in a border, we only need to open a new
3309 //paragraph if the paragraph has text frames that don't fly
3310 bAppend = HasCurrentParaFlys(true);
3312 else
3314 // Otherwise, we need to open a new paragraph if the paragraph
3315 // is empty or contains text frames or bookmarks
3316 bAppend =
3317 m_pPam->GetPoint()->GetContentIndex() ||
3318 HasCurrentParaFlys() ||
3319 HasCurrentParaBookmarks();
3321 if( bAppend )
3323 if( !m_pPam->GetPoint()->GetContentIndex() )
3325 //Set default to CJK and CTL
3326 m_xDoc->SetTextFormatColl( *m_pPam,
3327 m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_STANDARD) );
3328 SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
3330 HTMLAttr* pTmp =
3331 new HTMLAttr( *m_pPam->GetPoint(), aFontHeight, nullptr, std::shared_ptr<HTMLAttrTable>() );
3332 m_aSetAttrTab.push_back( pTmp );
3334 SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
3335 pTmp =
3336 new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCJK, nullptr, std::shared_ptr<HTMLAttrTable>() );
3337 m_aSetAttrTab.push_back( pTmp );
3339 SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
3340 pTmp =
3341 new HTMLAttr( *m_pPam->GetPoint(), aFontHeightCTL, nullptr, std::shared_ptr<HTMLAttrTable>() );
3342 m_aSetAttrTab.push_back( pTmp );
3344 pTmp = new HTMLAttr( *m_pPam->GetPoint(),
3345 SvxULSpaceItem( 0, 0, RES_UL_SPACE ), nullptr, std::shared_ptr<HTMLAttrTable>() );
3346 m_aSetAttrTab.push_front( pTmp ); // Position 0, since
3347 // something can be set by
3348 // the table end before
3350 AppendTextNode( AM_NOSPACE );
3351 bAppended = true;
3353 else if( !m_aParaAttrs.empty() )
3355 if( !bForceFrame )
3357 // The paragraph will be moved right behind the table.
3358 // That's why we remove all hard attributes of that paragraph
3360 for(HTMLAttr* i : m_aParaAttrs)
3361 i->Invalidate();
3364 m_aParaAttrs.clear();
3367 pSavePos = new SwPosition( *m_pPam->GetPoint() );
3369 else if( pCurTable->HasParentSection() )
3371 bParentLFStripped = StripTrailingLF() > 0;
3373 // Close paragraph resp. headers
3374 m_nOpenParaToken = HtmlTokenId::NONE;
3375 m_nFontStHeadStart = m_nFontStMin;
3377 // The hard attributes on that paragraph are never gonna be invalid anymore
3378 m_aParaAttrs.clear();
3381 // create a table context
3382 std::unique_ptr<HTMLTableContext> pTCntxt(
3383 new HTMLTableContext( pSavePos, m_nContextStMin,
3384 m_nContextStAttrMin ) );
3386 // end all open attributes and open them again behind the table
3387 std::optional<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts;
3388 if( !bForceFrame && (bTopTable || pCurTable->HasParentSection()) )
3390 SplitAttrTab(pTCntxt->m_xAttrTab, bTopTable);
3391 // If we reuse an already existing paragraph, we can't add
3392 // PostIts since the paragraph gets behind that table.
3393 // They're gonna be moved into the first paragraph of the table
3394 // If we have tables in tables, we also can't add PostIts to a
3395 // still empty paragraph, since it's not gonna be deleted that way
3396 if( (bTopTable && !bAppended) ||
3397 (!bTopTable && !bParentLFStripped &&
3398 !m_pPam->GetPoint()->GetContentIndex()) )
3399 pPostIts.emplace();
3400 SetAttr( bTopTable, bTopTable, pPostIts ? &*pPostIts : nullptr );
3402 else
3404 SaveAttrTab(pTCntxt->m_xAttrTab);
3405 if( bTopTable && !bAppended )
3407 pPostIts.emplace();
3408 SetAttr( true, true, &*pPostIts );
3411 m_bNoParSpace = false;
3413 // Save current numbering and turn it off
3414 pTCntxt->SetNumInfo( GetNumInfo() );
3415 GetNumInfo().Clear();
3416 pTCntxt->SavePREListingXMP( *this );
3418 if( bTopTable )
3420 if( bForceFrame )
3422 // the table should be put in a text frame
3424 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1>
3425 aFrameSet( m_xDoc->GetAttrPool() );
3426 if( !pCurTable->IsNewDoc() )
3427 Reader::ResetFrameFormatAttrs( aFrameSet );
3429 css::text::WrapTextMode eSurround = css::text::WrapTextMode_NONE;
3430 sal_Int16 eHori;
3432 switch( pCurTable->GetTableAdjust(true) )
3434 case SvxAdjust::Right:
3435 eHori = text::HoriOrientation::RIGHT;
3436 eSurround = css::text::WrapTextMode_LEFT;
3437 break;
3438 case SvxAdjust::Center:
3439 eHori = text::HoriOrientation::CENTER;
3440 break;
3441 case SvxAdjust::Left:
3442 eSurround = css::text::WrapTextMode_RIGHT;
3443 [[fallthrough]];
3444 default:
3445 eHori = text::HoriOrientation::LEFT;
3446 break;
3448 SetAnchorAndAdjustment( text::VertOrientation::NONE, eHori, aFrameSet,
3449 true );
3450 aFrameSet.Put( SwFormatSurround(eSurround) );
3452 constexpr tools::Long constTwips_100mm = o3tl::convert(tools::Long(100), o3tl::Length::mm, o3tl::Length::twip);
3454 SwFormatFrameSize aFrameSize( SwFrameSize::Variable, constTwips_100mm, MINLAY );
3455 aFrameSize.SetWidthPercent( 100 );
3456 aFrameSet.Put( aFrameSize );
3458 sal_uInt16 nSpace = pCurTable->GetHSpace();
3459 if( nSpace )
3460 aFrameSet.Put( SvxLRSpaceItem(nSpace, nSpace, 0, RES_LR_SPACE) );
3461 nSpace = pCurTable->GetVSpace();
3462 if( nSpace )
3463 aFrameSet.Put( SvxULSpaceItem(nSpace,nSpace, RES_UL_SPACE) );
3465 RndStdIds eAnchorId = aFrameSet.
3466 Get( RES_ANCHOR ).
3467 GetAnchorId();
3468 SwFrameFormat *pFrameFormat = m_xDoc->MakeFlySection(
3469 eAnchorId, m_pPam->GetPoint(), &aFrameSet );
3471 pTCntxt->SetFrameFormat( pFrameFormat );
3472 const SwFormatContent& rFlyContent = pFrameFormat->GetContent();
3473 m_pPam->GetPoint()->Assign( *rFlyContent.GetContentIdx() );
3474 m_xDoc->GetNodes().GoNext( m_pPam->GetPoint() );
3477 // create a SwTable with a box and set the PaM to the content of
3478 // the box section (the adjustment parameter is a dummy for now
3479 // and will be corrected later)
3480 OSL_ENSURE( !m_pPam->GetPoint()->GetContentIndex(),
3481 "The paragraph after the table is not empty!" );
3482 const SwTable* pSwTable = m_xDoc->InsertTable(
3483 SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ),
3484 *m_pPam->GetPoint(), 1, 1, text::HoriOrientation::LEFT );
3485 SwFrameFormat *pFrameFormat = pSwTable ? pSwTable->GetFrameFormat() : nullptr;
3487 if( bForceFrame )
3489 SwNodeIndex aDstIdx( m_pPam->GetPoint()->GetNode() );
3490 m_pPam->Move( fnMoveBackward );
3491 m_xDoc->GetNodes().Delete( aDstIdx );
3493 else
3495 if (bStyleParsed && pFrameFormat)
3497 m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo );
3498 pFrameFormat->SetFormatAttr( aItemSet );
3500 m_pPam->Move( fnMoveBackward );
3503 SwNode const*const pNd = & m_pPam->GetPoint()->GetNode();
3504 SwTextNode *const pOldTextNd = (!bAppended && !bForceFrame) ?
3505 pSavePos->GetNode().GetTextNode() : nullptr;
3507 if (pFrameFormat && pOldTextNd)
3509 const SwFormatPageDesc* pPageDescItem = pOldTextNd->GetSwAttrSet()
3510 .GetItemIfSet( RES_PAGEDESC, false );
3511 if( pPageDescItem && pPageDescItem->GetPageDesc() )
3513 pFrameFormat->SetFormatAttr( *pPageDescItem );
3514 pOldTextNd->ResetAttr( RES_PAGEDESC );
3517 if( const SvxFormatBreakItem* pBreakItem = pOldTextNd->GetSwAttrSet()
3518 .GetItemIfSet( RES_BREAK ) )
3520 switch( pBreakItem->GetBreak() )
3522 case SvxBreak::PageBefore:
3523 case SvxBreak::PageAfter:
3524 case SvxBreak::PageBoth:
3525 pFrameFormat->SetFormatAttr( *pBreakItem );
3526 pOldTextNd->ResetAttr( RES_BREAK );
3527 break;
3528 default:
3529 break;
3534 if( !bAppended && pPostIts )
3536 // set still-existing PostIts to the first paragraph of the table
3537 InsertAttrs( std::move(*pPostIts) );
3538 pPostIts.reset();
3541 pTCntxt->SetTableNode( const_cast<SwTableNode *>(pNd->FindTableNode()) );
3543 auto pTableNode = pTCntxt->GetTableNode();
3544 pCurTable->SetTable( pTableNode, std::move(pTCntxt),
3545 nLeftSpace, nRightSpace,
3546 pSwTable, bForceFrame );
3548 OSL_ENSURE( !pPostIts, "unused PostIts" );
3550 else
3552 // still open sections need to be deleted
3553 if( EndSections( bParentLFStripped ) )
3554 bParentLFStripped = false;
3556 if( pCurTable->HasParentSection() )
3558 // after that, we remove a possibly redundant empty paragraph,
3559 // but only if it was empty before we stripped the LFs
3560 if( !bParentLFStripped )
3561 StripTrailingPara();
3563 if( pPostIts )
3565 // move still existing PostIts to the end of the current paragraph
3566 InsertAttrs( std::move(*pPostIts) );
3567 pPostIts.reset();
3571 SwNode const*const pNd = & m_pPam->GetPoint()->GetNode();
3572 const SwStartNode *pStNd = (m_xTable->m_bFirstCell ? pNd->FindTableNode()
3573 : pNd->FindTableBoxStartNode() );
3575 pCurTable->SetTable( pStNd, std::move(pTCntxt), nLeftSpace, nRightSpace );
3578 // Freeze the context stack, since there could be attributes set
3579 // outside of cells. Can't happen earlier, since there may be
3580 // searches in the stack
3581 m_nContextStMin = m_aContexts.size();
3582 m_nContextStAttrMin = m_nContextStMin;
3585 xSaveStruct.reset(new CellSaveStruct(*this, pCurTable, bHead, bReadOptions));
3587 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
3588 SaveState( HtmlTokenId::NONE );
3591 if( nToken == HtmlTokenId::NONE )
3592 nToken = GetNextToken(); // Token after <TABLE>
3594 bool bDone = false;
3595 while( (IsParserWorking() && !bDone) || bPending )
3597 SaveState( nToken );
3599 nToken = FilterToken( nToken );
3601 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken || xSaveStruct->IsInSection(),
3602 "Where is the section??" );
3603 if( m_vPendingStack.empty() && m_bCallNextToken && xSaveStruct->IsInSection() )
3605 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
3606 NextToken( nToken );
3608 else switch( nToken )
3610 case HtmlTokenId::TABLEHEADER_ON:
3611 case HtmlTokenId::TABLEDATA_ON:
3612 case HtmlTokenId::TABLEROW_ON:
3613 case HtmlTokenId::TABLEROW_OFF:
3614 case HtmlTokenId::THEAD_ON:
3615 case HtmlTokenId::THEAD_OFF:
3616 case HtmlTokenId::TFOOT_ON:
3617 case HtmlTokenId::TFOOT_OFF:
3618 case HtmlTokenId::TBODY_ON:
3619 case HtmlTokenId::TBODY_OFF:
3620 case HtmlTokenId::TABLE_OFF:
3621 SkipToken();
3622 [[fallthrough]];
3623 case HtmlTokenId::TABLEHEADER_OFF:
3624 case HtmlTokenId::TABLEDATA_OFF:
3625 bDone = true;
3626 break;
3627 case HtmlTokenId::TABLE_ON:
3629 bool bHasToFly = false;
3630 SvxAdjust eTabAdjust = SvxAdjust::End;
3631 if( m_vPendingStack.empty() )
3633 // only if we create a new table, but not if we're still
3634 // reading in the table after a Pending
3635 xSaveStruct->m_xTable = m_xTable;
3637 // HACK: create a section for a table that goes in a text frame
3638 if( !xSaveStruct->IsInSection() )
3640 // The loop needs to be forward, since the
3641 // first option always wins
3642 bool bNeedsSection = false;
3643 const HTMLOptions& rHTMLOptions = GetOptions();
3644 for (const auto & rOption : rHTMLOptions)
3646 if( HtmlOptionId::ALIGN==rOption.GetToken() )
3648 SvxAdjust eAdjust = rOption.GetEnum( aHTMLPAlignTable, SvxAdjust::End );
3649 bNeedsSection = SvxAdjust::Left == eAdjust ||
3650 SvxAdjust::Right == eAdjust;
3651 break;
3654 if( bNeedsSection )
3656 xSaveStruct->AddContents(
3657 InsertTableContents(bHead ) );
3660 else
3662 // If Flys are anchored in the current paragraph,
3663 // the table needs to get in a text frame
3664 bHasToFly = HasCurrentParaFlys(false,true);
3667 // There could be a section in the cell
3668 eTabAdjust = m_xAttrTab->pAdjust
3669 ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()).
3670 GetAdjust()
3671 : SvxAdjust::End;
3674 std::shared_ptr<HTMLTable> xSubTable = BuildTable(eTabAdjust,
3675 bHead,
3676 xSaveStruct->IsInSection(),
3677 bHasToFly);
3678 if( SvParserState::Pending != GetStatus() )
3680 // Only if the table is really complete
3681 if (xSubTable)
3683 OSL_ENSURE( xSubTable->GetTableAdjust(false)!= SvxAdjust::Left &&
3684 xSubTable->GetTableAdjust(false)!= SvxAdjust::Right,
3685 "left or right aligned tables belong in frames" );
3687 auto& rParentContents = xSubTable->GetParentContents();
3688 if (rParentContents)
3690 OSL_ENSURE( !xSaveStruct->IsInSection(),
3691 "Where is the section" );
3693 // If there's no table coming, we have a section
3694 xSaveStruct->AddContents(std::move(rParentContents));
3697 const SwStartNode *pCapStNd =
3698 xSubTable->GetCaptionStartNode();
3700 if (xSubTable->GetContext())
3702 OSL_ENSURE( !xSubTable->GetContext()->GetFrameFormat(),
3703 "table in frame" );
3705 if( pCapStNd && xSubTable->IsTopCaption() )
3707 xSaveStruct->AddContents(
3708 std::make_unique<HTMLTableCnts>(pCapStNd) );
3711 xSaveStruct->AddContents(
3712 std::make_unique<HTMLTableCnts>(xSubTable) );
3714 if( pCapStNd && !xSubTable->IsTopCaption() )
3716 xSaveStruct->AddContents(
3717 std::make_unique<HTMLTableCnts>(pCapStNd) );
3720 // We don't have a section anymore
3721 xSaveStruct->ClearIsInSection();
3723 else if( pCapStNd )
3725 // Since we can't delete this section (it might
3726 // belong to the first box), we'll add it
3727 xSaveStruct->AddContents(
3728 std::make_unique<HTMLTableCnts>(pCapStNd) );
3730 // We don't have a section anymore
3731 xSaveStruct->ClearIsInSection();
3735 m_xTable = xSaveStruct->m_xTable;
3738 break;
3740 case HtmlTokenId::NOBR_ON:
3741 // HACK for MS: Is the <NOBR> at the start of the cell?
3742 xSaveStruct->StartNoBreak( *m_pPam->GetPoint() );
3743 break;
3745 case HtmlTokenId::NOBR_OFF:
3746 xSaveStruct->EndNoBreak( *m_pPam->GetPoint() );
3747 break;
3749 case HtmlTokenId::COMMENT:
3750 // Spaces are not gonna be deleted with comment fields,
3751 // and we don't want a new cell for a comment
3752 NextToken( nToken );
3753 break;
3755 case HtmlTokenId::MARQUEE_ON:
3756 if( !xSaveStruct->IsInSection() )
3758 // create a new section, the PaM is gonna be there
3759 xSaveStruct->AddContents(
3760 InsertTableContents( bHead ) );
3762 m_bCallNextToken = true;
3763 NewMarquee( pCurTable );
3764 break;
3766 case HtmlTokenId::TEXTTOKEN:
3767 // Don't add a section for an empty string
3768 if( !xSaveStruct->IsInSection() && 1==aToken.getLength() &&
3769 ' '==aToken[0] )
3770 break;
3771 [[fallthrough]];
3772 default:
3773 if( !xSaveStruct->IsInSection() )
3775 // add a new section, the PaM's gonna be there
3776 xSaveStruct->AddContents(
3777 InsertTableContents( bHead ) );
3780 if( IsParserWorking() || bPending )
3781 NextToken( nToken );
3782 break;
3785 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
3786 "SwHTMLParser::BuildTableCell: There is a PendStack again" );
3787 bPending = false;
3788 if( IsParserWorking() )
3789 SaveState( HtmlTokenId::NONE );
3791 if( !bDone )
3792 nToken = GetNextToken();
3795 if( SvParserState::Pending == GetStatus() )
3797 m_vPendingStack.emplace_back( bHead ? HtmlTokenId::TABLEHEADER_ON
3798 : HtmlTokenId::TABLEDATA_ON );
3799 m_vPendingStack.back().pData = std::move(xSaveStruct);
3801 return;
3804 // If the content of the cell was empty, we need to create an empty content
3805 // We also create an empty content if the cell ended with a table and had no
3806 // COL tags. Otherwise, it was probably exported by us and we don't
3807 // want to have an additional paragraph
3808 if( !xSaveStruct->HasFirstContents() ||
3809 (!xSaveStruct->IsInSection() && !pCurTable->HasColTags()) )
3811 OSL_ENSURE( xSaveStruct->HasFirstContents() ||
3812 !xSaveStruct->IsInSection(),
3813 "Section or not, that is the question here" );
3814 const SwStartNode *pStNd =
3815 InsertTableSection( static_cast< sal_uInt16 >(xSaveStruct->IsHeaderCell()
3816 ? RES_POOLCOLL_TABLE_HDLN
3817 : RES_POOLCOLL_TABLE ));
3819 if (!pStNd)
3820 eState = SvParserState::Error;
3821 else
3823 const SwEndNode *pEndNd = pStNd->EndOfSectionNode();
3824 SwContentNode *pCNd = m_xDoc->GetNodes()[pEndNd->GetIndex()-1] ->GetContentNode();
3825 if (!pCNd)
3826 eState = SvParserState::Error;
3827 else
3829 //Added defaults to CJK and CTL
3830 SvxFontHeightItem aFontHeight( 40, 100, RES_CHRATR_FONTSIZE );
3831 pCNd->SetAttr( aFontHeight );
3832 SvxFontHeightItem aFontHeightCJK( 40, 100, RES_CHRATR_CJK_FONTSIZE );
3833 pCNd->SetAttr( aFontHeightCJK );
3834 SvxFontHeightItem aFontHeightCTL( 40, 100, RES_CHRATR_CTL_FONTSIZE );
3835 pCNd->SetAttr( aFontHeightCTL );
3839 xSaveStruct->AddContents( std::make_unique<HTMLTableCnts>(pStNd) );
3840 xSaveStruct->ClearIsInSection();
3843 if( xSaveStruct->IsInSection() )
3845 xSaveStruct->CheckNoBreak( *m_pPam->GetPoint() );
3847 // End all open contexts. We'll take AttrMin because nContextStMin might
3848 // have been modified. Since it's gonna be restored by EndContext, it's okay
3849 while( m_aContexts.size() > m_nContextStAttrMin+1 )
3851 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3852 EndContext(xCntxt.get());
3855 // Remove LFs at the paragraph end
3856 if (StripTrailingLF() == 0 && !m_pPam->GetPoint()->GetContentIndex())
3858 HTMLTableContext* pTableContext = m_xTable ? m_xTable->GetContext() : nullptr;
3859 SwPosition* pSavedPos = pTableContext ? pTableContext->GetPos() : nullptr;
3860 const bool bDeleteSafe = !pSavedPos || pSavedPos->GetNode() != m_pPam->GetPoint()->GetNode();
3861 if (bDeleteSafe)
3862 StripTrailingPara();
3865 // If there was an adjustment set for the cell, we need to close it
3866 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3867 if (xCntxt)
3868 EndContext(xCntxt.get());
3870 else
3872 // Close all still open contexts
3873 while( m_aContexts.size() > m_nContextStAttrMin )
3875 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
3876 if (!xCntxt)
3877 break;
3878 ClearContext(xCntxt.get());
3882 // end an enumeration
3883 GetNumInfo().Clear();
3885 SetAttr( false );
3887 xSaveStruct->InsertCell( *this, pCurTable );
3889 // we're probably before a <TH>, <TD>, <TR> or </TABLE>
3890 xSaveStruct.reset();
3893 namespace {
3895 class RowSaveStruct : public SwPendingData
3897 public:
3898 SvxAdjust eAdjust;
3899 sal_Int16 eVertOri;
3900 bool bHasCells;
3902 RowSaveStruct() :
3903 eAdjust( SvxAdjust::End ), eVertOri( text::VertOrientation::TOP ), bHasCells( false )
3909 void SwHTMLParser::BuildTableRow( HTMLTable *pCurTable, bool bReadOptions,
3910 SvxAdjust eGrpAdjust,
3911 sal_Int16 eGrpVertOri )
3913 // <TR> was already read
3915 if( !IsParserWorking() && m_vPendingStack.empty() )
3916 return;
3918 HtmlTokenId nToken = HtmlTokenId::NONE;
3919 std::unique_ptr<RowSaveStruct> xSaveStruct;
3921 bool bPending = false;
3922 if( !m_vPendingStack.empty() )
3924 xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
3926 m_vPendingStack.pop_back();
3927 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
3928 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
3930 SaveState( nToken );
3932 else
3934 SvxAdjust eAdjust = eGrpAdjust;
3935 sal_Int16 eVertOri = eGrpVertOri;
3936 Color aBGColor;
3937 OUString aBGImage, aStyle, aId, aClass;
3938 bool bBGColor = false;
3939 xSaveStruct.reset(new RowSaveStruct);
3941 if( bReadOptions )
3943 const HTMLOptions& rHTMLOptions = GetOptions();
3944 for (size_t i = rHTMLOptions.size(); i; )
3946 const HTMLOption& rOption = rHTMLOptions[--i];
3947 switch( rOption.GetToken() )
3949 case HtmlOptionId::ID:
3950 aId = rOption.GetString();
3951 break;
3952 case HtmlOptionId::ALIGN:
3953 eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
3954 break;
3955 case HtmlOptionId::VALIGN:
3956 eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri );
3957 break;
3958 case HtmlOptionId::BGCOLOR:
3959 // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/>TH> like Netscape
3960 // *really* not on other tags
3961 if( !rOption.GetString().isEmpty() )
3963 rOption.GetColor( aBGColor );
3964 bBGColor = true;
3966 break;
3967 case HtmlOptionId::BACKGROUND:
3968 aBGImage = rOption.GetString();
3969 break;
3970 case HtmlOptionId::STYLE:
3971 aStyle = rOption.GetString();
3972 break;
3973 case HtmlOptionId::CLASS:
3974 aClass= rOption.GetString();
3975 break;
3976 default: break;
3981 if( !aId.isEmpty() )
3982 InsertBookmark( aId );
3984 std::unique_ptr<SvxBrushItem> xBrushItem(
3985 CreateBrushItem( bBGColor ? &aBGColor : nullptr, aBGImage, aStyle,
3986 aId, aClass ));
3987 pCurTable->OpenRow(eAdjust, eVertOri, xBrushItem);
3988 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
3989 SaveState( HtmlTokenId::NONE );
3992 if( nToken == HtmlTokenId::NONE )
3993 nToken = GetNextToken();
3995 bool bDone = false;
3996 while( (IsParserWorking() && !bDone) || bPending )
3998 SaveState( nToken );
4000 nToken = FilterToken( nToken );
4002 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
4003 pCurTable->GetContext() || pCurTable->HasParentSection(),
4004 "Where is the section??" );
4005 if( m_vPendingStack.empty() && m_bCallNextToken &&
4006 (pCurTable->GetContext() || pCurTable->HasParentSection()) )
4008 /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
4009 NextToken( nToken );
4011 else switch( nToken )
4013 case HtmlTokenId::TABLE_ON:
4014 if( !pCurTable->GetContext() )
4016 SkipToken();
4017 bDone = true;
4020 break;
4021 case HtmlTokenId::TABLEROW_ON:
4022 case HtmlTokenId::THEAD_ON:
4023 case HtmlTokenId::THEAD_OFF:
4024 case HtmlTokenId::TBODY_ON:
4025 case HtmlTokenId::TBODY_OFF:
4026 case HtmlTokenId::TFOOT_ON:
4027 case HtmlTokenId::TFOOT_OFF:
4028 case HtmlTokenId::TABLE_OFF:
4029 SkipToken();
4030 [[fallthrough]];
4031 case HtmlTokenId::TABLEROW_OFF:
4032 bDone = true;
4033 break;
4034 case HtmlTokenId::TABLEHEADER_ON:
4035 case HtmlTokenId::TABLEDATA_ON:
4036 BuildTableCell( pCurTable, true, HtmlTokenId::TABLEHEADER_ON==nToken );
4037 if( SvParserState::Pending != GetStatus() )
4039 xSaveStruct->bHasCells = true;
4040 bDone = m_xTable->IsOverflowing();
4042 break;
4043 case HtmlTokenId::CAPTION_ON:
4044 BuildTableCaption( pCurTable );
4045 bDone = m_xTable->IsOverflowing();
4046 break;
4047 case HtmlTokenId::CAPTION_OFF:
4048 case HtmlTokenId::TABLEHEADER_OFF:
4049 case HtmlTokenId::TABLEDATA_OFF:
4050 case HtmlTokenId::COLGROUP_ON:
4051 case HtmlTokenId::COLGROUP_OFF:
4052 case HtmlTokenId::COL_ON:
4053 case HtmlTokenId::COL_OFF:
4054 // Where no cell started, there can't be a cell ending
4055 // all the other tokens are bogus anyway and only break the table
4056 break;
4057 case HtmlTokenId::MULTICOL_ON:
4058 // we can't add columned text frames here
4059 break;
4060 case HtmlTokenId::FORM_ON:
4061 NewForm( false ); // don't create a new paragraph
4062 break;
4063 case HtmlTokenId::FORM_OFF:
4064 EndForm( false ); // don't create a new paragraph
4065 break;
4066 case HtmlTokenId::COMMENT:
4067 NextToken( nToken );
4068 break;
4069 case HtmlTokenId::MAP_ON:
4070 // an image map doesn't add anything, so we can parse it without a cell
4071 NextToken( nToken );
4072 break;
4073 case HtmlTokenId::TEXTTOKEN:
4074 if( (pCurTable->GetContext() ||
4075 !pCurTable->HasParentSection()) &&
4076 1==aToken.getLength() && ' '==aToken[0] )
4077 break;
4078 [[fallthrough]];
4079 default:
4080 pCurTable->MakeParentContents();
4081 NextToken( nToken );
4082 break;
4085 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
4086 "SwHTMLParser::BuildTableRow: There is a PendStack again" );
4087 bPending = false;
4088 if( IsParserWorking() )
4089 SaveState( HtmlTokenId::NONE );
4091 if( !bDone )
4092 nToken = GetNextToken();
4095 if( SvParserState::Pending == GetStatus() )
4097 m_vPendingStack.emplace_back( HtmlTokenId::TABLEROW_ON );
4098 m_vPendingStack.back().pData = std::move(xSaveStruct);
4100 else
4102 pCurTable->CloseRow(!xSaveStruct->bHasCells);
4103 xSaveStruct.reset();
4106 // we're probably before <TR> or </TABLE>
4109 void SwHTMLParser::BuildTableSection( HTMLTable *pCurTable,
4110 bool bReadOptions,
4111 bool bHead )
4113 // <THEAD>, <TBODY> resp. <TFOOT> were read already
4114 if( !IsParserWorking() && m_vPendingStack.empty() )
4115 return;
4117 HtmlTokenId nToken = HtmlTokenId::NONE;
4118 bool bPending = false;
4119 std::unique_ptr<RowSaveStruct> xSaveStruct;
4121 if( !m_vPendingStack.empty() )
4123 xSaveStruct.reset(static_cast<RowSaveStruct*>(m_vPendingStack.back().pData.release()));
4125 m_vPendingStack.pop_back();
4126 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4127 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4129 SaveState( nToken );
4131 else
4133 xSaveStruct.reset(new RowSaveStruct);
4135 if( bReadOptions )
4137 const HTMLOptions& rHTMLOptions = GetOptions();
4138 for (size_t i = rHTMLOptions.size(); i; )
4140 const HTMLOption& rOption = rHTMLOptions[--i];
4141 switch( rOption.GetToken() )
4143 case HtmlOptionId::ID:
4144 InsertBookmark( rOption.GetString() );
4145 break;
4146 case HtmlOptionId::ALIGN:
4147 xSaveStruct->eAdjust =
4148 rOption.GetEnum( aHTMLPAlignTable, xSaveStruct->eAdjust );
4149 break;
4150 case HtmlOptionId::VALIGN:
4151 xSaveStruct->eVertOri =
4152 rOption.GetEnum( aHTMLTableVAlignTable,
4153 xSaveStruct->eVertOri );
4154 break;
4155 default: break;
4160 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4161 SaveState( HtmlTokenId::NONE );
4164 if( nToken == HtmlTokenId::NONE )
4165 nToken = GetNextToken();
4167 bool bDone = false;
4168 while( (IsParserWorking() && !bDone) || bPending )
4170 SaveState( nToken );
4172 nToken = FilterToken( nToken );
4174 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
4175 pCurTable->GetContext() || pCurTable->HasParentSection(),
4176 "Where is the section?" );
4177 if( m_vPendingStack.empty() && m_bCallNextToken &&
4178 (pCurTable->GetContext() || pCurTable->HasParentSection()) )
4180 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
4181 NextToken( nToken );
4183 else switch( nToken )
4185 case HtmlTokenId::TABLE_ON:
4186 if( !pCurTable->GetContext() )
4188 SkipToken();
4189 bDone = true;
4192 break;
4193 case HtmlTokenId::THEAD_ON:
4194 case HtmlTokenId::TFOOT_ON:
4195 case HtmlTokenId::TBODY_ON:
4196 case HtmlTokenId::TABLE_OFF:
4197 SkipToken();
4198 [[fallthrough]];
4199 case HtmlTokenId::THEAD_OFF:
4200 case HtmlTokenId::TBODY_OFF:
4201 case HtmlTokenId::TFOOT_OFF:
4202 bDone = true;
4203 break;
4204 case HtmlTokenId::CAPTION_ON:
4205 BuildTableCaption( pCurTable );
4206 bDone = m_xTable->IsOverflowing();
4207 break;
4208 case HtmlTokenId::CAPTION_OFF:
4209 break;
4210 case HtmlTokenId::TABLEHEADER_ON:
4211 case HtmlTokenId::TABLEDATA_ON:
4212 SkipToken();
4213 BuildTableRow( pCurTable, false, xSaveStruct->eAdjust,
4214 xSaveStruct->eVertOri );
4215 bDone = m_xTable->IsOverflowing();
4216 break;
4217 case HtmlTokenId::TABLEROW_ON:
4218 BuildTableRow( pCurTable, true, xSaveStruct->eAdjust,
4219 xSaveStruct->eVertOri );
4220 bDone = m_xTable->IsOverflowing();
4221 break;
4222 case HtmlTokenId::MULTICOL_ON:
4223 // we can't add columned text frames here
4224 break;
4225 case HtmlTokenId::FORM_ON:
4226 NewForm( false ); // don't create a new paragraph
4227 break;
4228 case HtmlTokenId::FORM_OFF:
4229 EndForm( false ); // don't create a new paragraph
4230 break;
4231 case HtmlTokenId::TEXTTOKEN:
4232 // blank strings may be a series of CR+LF and no text
4233 if( (pCurTable->GetContext() ||
4234 !pCurTable->HasParentSection()) &&
4235 1==aToken.getLength() && ' ' == aToken[0] )
4236 break;
4237 [[fallthrough]];
4238 default:
4239 pCurTable->MakeParentContents();
4240 NextToken( nToken );
4243 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
4244 "SwHTMLParser::BuildTableSection: There is a PendStack again" );
4245 bPending = false;
4246 if( IsParserWorking() )
4247 SaveState( HtmlTokenId::NONE );
4249 if( !bDone )
4250 nToken = GetNextToken();
4253 if( SvParserState::Pending == GetStatus() )
4255 m_vPendingStack.emplace_back( bHead ? HtmlTokenId::THEAD_ON
4256 : HtmlTokenId::TBODY_ON );
4257 m_vPendingStack.back().pData = std::move(xSaveStruct);
4259 else
4261 pCurTable->CloseSection( bHead );
4262 xSaveStruct.reset();
4265 // now we stand (perhaps) in front of <TBODY>,... or </TABLE>
4268 namespace {
4270 struct TableColGrpSaveStruct : public SwPendingData
4272 sal_uInt16 nColGrpSpan;
4273 sal_uInt16 nColGrpWidth;
4274 bool bRelColGrpWidth;
4275 SvxAdjust eColGrpAdjust;
4276 sal_Int16 eColGrpVertOri;
4278 inline TableColGrpSaveStruct();
4280 inline void CloseColGroup( HTMLTable *pTable );
4285 inline TableColGrpSaveStruct::TableColGrpSaveStruct() :
4286 nColGrpSpan( 1 ), nColGrpWidth( 0 ),
4287 bRelColGrpWidth( false ), eColGrpAdjust( SvxAdjust::End ),
4288 eColGrpVertOri( text::VertOrientation::TOP )
4291 inline void TableColGrpSaveStruct::CloseColGroup( HTMLTable *pTable )
4293 pTable->CloseColGroup( nColGrpSpan, nColGrpWidth,
4294 bRelColGrpWidth, eColGrpAdjust, eColGrpVertOri );
4297 void SwHTMLParser::BuildTableColGroup( HTMLTable *pCurTable,
4298 bool bReadOptions )
4300 // <COLGROUP> was read already if bReadOptions is set
4302 if( !IsParserWorking() && m_vPendingStack.empty() )
4303 return;
4305 HtmlTokenId nToken = HtmlTokenId::NONE;
4306 bool bPending = false;
4307 std::unique_ptr<TableColGrpSaveStruct> pSaveStruct;
4309 if( !m_vPendingStack.empty() )
4311 pSaveStruct.reset(static_cast<TableColGrpSaveStruct*>(m_vPendingStack.back().pData.release()));
4314 m_vPendingStack.pop_back();
4315 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4316 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4318 SaveState( nToken );
4320 else
4323 pSaveStruct.reset(new TableColGrpSaveStruct);
4324 if( bReadOptions )
4326 const HTMLOptions& rColGrpOptions = GetOptions();
4327 for (size_t i = rColGrpOptions.size(); i; )
4329 const HTMLOption& rOption = rColGrpOptions[--i];
4330 switch( rOption.GetToken() )
4332 case HtmlOptionId::ID:
4333 InsertBookmark( rOption.GetString() );
4334 break;
4335 case HtmlOptionId::SPAN:
4336 pSaveStruct->nColGrpSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4337 if (pSaveStruct->nColGrpSpan > 256)
4339 SAL_INFO("sw.html", "ignoring huge SPAN " << pSaveStruct->nColGrpSpan);
4340 pSaveStruct->nColGrpSpan = 1;
4342 break;
4343 case HtmlOptionId::WIDTH:
4344 pSaveStruct->nColGrpWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4345 pSaveStruct->bRelColGrpWidth =
4346 (rOption.GetString().indexOf('*') != -1);
4347 break;
4348 case HtmlOptionId::ALIGN:
4349 pSaveStruct->eColGrpAdjust =
4350 rOption.GetEnum( aHTMLPAlignTable, pSaveStruct->eColGrpAdjust );
4351 break;
4352 case HtmlOptionId::VALIGN:
4353 pSaveStruct->eColGrpVertOri =
4354 rOption.GetEnum( aHTMLTableVAlignTable,
4355 pSaveStruct->eColGrpVertOri );
4356 break;
4357 default: break;
4361 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4362 SaveState( HtmlTokenId::NONE );
4365 if( nToken == HtmlTokenId::NONE )
4366 nToken = GetNextToken(); // naechstes Token
4368 bool bDone = false;
4369 while( (IsParserWorking() && !bDone) || bPending )
4371 SaveState( nToken );
4373 nToken = FilterToken( nToken );
4375 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
4376 pCurTable->GetContext() || pCurTable->HasParentSection(),
4377 "Where is the section?" );
4378 if( m_vPendingStack.empty() && m_bCallNextToken &&
4379 (pCurTable->GetContext() || pCurTable->HasParentSection()) )
4381 // Call NextToken directly (e.g. ignore the content of floating frames or applets)
4382 NextToken( nToken );
4384 else switch( nToken )
4386 case HtmlTokenId::TABLE_ON:
4387 if( !pCurTable->GetContext() )
4389 SkipToken();
4390 bDone = true;
4393 break;
4394 case HtmlTokenId::COLGROUP_ON:
4395 case HtmlTokenId::THEAD_ON:
4396 case HtmlTokenId::TFOOT_ON:
4397 case HtmlTokenId::TBODY_ON:
4398 case HtmlTokenId::TABLEROW_ON:
4399 case HtmlTokenId::TABLE_OFF:
4400 SkipToken();
4401 [[fallthrough]];
4402 case HtmlTokenId::COLGROUP_OFF:
4403 bDone = true;
4404 break;
4405 case HtmlTokenId::COL_ON:
4407 sal_uInt16 nColSpan = 1;
4408 sal_uInt16 nColWidth = pSaveStruct->nColGrpWidth;
4409 bool bRelColWidth = pSaveStruct->bRelColGrpWidth;
4410 SvxAdjust eColAdjust = pSaveStruct->eColGrpAdjust;
4411 sal_Int16 eColVertOri = pSaveStruct->eColGrpVertOri;
4413 const HTMLOptions& rColOptions = GetOptions();
4414 for (size_t i = rColOptions.size(); i; )
4416 const HTMLOption& rOption = rColOptions[--i];
4417 switch( rOption.GetToken() )
4419 case HtmlOptionId::ID:
4420 InsertBookmark( rOption.GetString() );
4421 break;
4422 case HtmlOptionId::SPAN:
4423 nColSpan = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4424 if (nColSpan > 256)
4426 SAL_INFO("sw.html", "ignoring huge SPAN " << nColSpan);
4427 nColSpan = 1;
4429 break;
4430 case HtmlOptionId::WIDTH:
4431 nColWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4432 bRelColWidth =
4433 (rOption.GetString().indexOf('*') != -1);
4434 break;
4435 case HtmlOptionId::ALIGN:
4436 eColAdjust = rOption.GetEnum( aHTMLPAlignTable, eColAdjust );
4437 break;
4438 case HtmlOptionId::VALIGN:
4439 eColVertOri =
4440 rOption.GetEnum( aHTMLTableVAlignTable, eColVertOri );
4441 break;
4442 default: break;
4445 pCurTable->InsertCol( nColSpan, nColWidth, bRelColWidth,
4446 eColAdjust, eColVertOri );
4448 // the attributes in <COLGRP> should be ignored, if there are <COL> elements
4449 pSaveStruct->nColGrpSpan = 0;
4451 break;
4452 case HtmlTokenId::COL_OFF:
4453 break; // Ignore
4454 case HtmlTokenId::MULTICOL_ON:
4455 // we can't add columned text frames here
4456 break;
4457 case HtmlTokenId::TEXTTOKEN:
4458 if( (pCurTable->GetContext() ||
4459 !pCurTable->HasParentSection()) &&
4460 1==aToken.getLength() && ' '==aToken[0] )
4461 break;
4462 [[fallthrough]];
4463 default:
4464 pCurTable->MakeParentContents();
4465 NextToken( nToken );
4468 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
4469 "SwHTMLParser::BuildTableColGrp: There is a PendStack again" );
4470 bPending = false;
4471 if( IsParserWorking() )
4472 SaveState( HtmlTokenId::NONE );
4474 if( !bDone )
4475 nToken = GetNextToken();
4478 if( SvParserState::Pending == GetStatus() )
4480 m_vPendingStack.emplace_back( HtmlTokenId::COL_ON );
4481 m_vPendingStack.back().pData = std::move(pSaveStruct);
4483 else
4485 pSaveStruct->CloseColGroup( pCurTable );
4489 class CaptionSaveStruct : public SectionSaveStruct
4491 SwPosition m_aSavePos;
4492 SwHTMLNumRuleInfo m_aNumRuleInfo; // valid numbering
4494 public:
4496 std::shared_ptr<HTMLAttrTable> m_xAttrTab; // attributes
4498 CaptionSaveStruct( SwHTMLParser& rParser, SwPosition aPos ) :
4499 SectionSaveStruct( rParser ), m_aSavePos(std::move( aPos )),
4500 m_xAttrTab(std::make_shared<HTMLAttrTable>())
4502 rParser.SaveAttrTab(m_xAttrTab);
4504 // The current numbering was remembered and just needs to be closed
4505 m_aNumRuleInfo.Set( rParser.GetNumInfo() );
4506 rParser.GetNumInfo().Clear();
4509 const SwPosition& GetPos() const { return m_aSavePos; }
4511 void RestoreAll( SwHTMLParser& rParser )
4513 // Recover the old stack
4514 Restore( rParser );
4516 // Recover the old attribute tables
4517 rParser.RestoreAttrTab(m_xAttrTab);
4519 // Re-open the old numbering
4520 rParser.GetNumInfo().Set( m_aNumRuleInfo );
4524 void SwHTMLParser::BuildTableCaption( HTMLTable *pCurTable )
4526 // <CAPTION> was read already
4528 if( !IsParserWorking() && m_vPendingStack.empty() )
4529 return;
4531 HtmlTokenId nToken = HtmlTokenId::NONE;
4532 std::unique_ptr<CaptionSaveStruct> xSaveStruct;
4534 if( !m_vPendingStack.empty() )
4536 xSaveStruct.reset(static_cast<CaptionSaveStruct*>(m_vPendingStack.back().pData.release()));
4538 m_vPendingStack.pop_back();
4539 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4540 OSL_ENSURE( m_vPendingStack.empty(), "Where does a PendStack coming from?" );
4542 SaveState( nToken );
4544 else
4546 if (m_xTable->IsOverflowing())
4548 SaveState( HtmlTokenId::NONE );
4549 return;
4552 bool bTop = true;
4553 const HTMLOptions& rHTMLOptions = GetOptions();
4554 for ( size_t i = rHTMLOptions.size(); i; )
4556 const HTMLOption& rOption = rHTMLOptions[--i];
4557 if( HtmlOptionId::ALIGN == rOption.GetToken() )
4559 if (rOption.GetString().equalsIgnoreAsciiCase(
4560 OOO_STRING_SVTOOLS_HTML_VA_bottom))
4562 bTop = false;
4567 // Remember old PaM position
4568 xSaveStruct.reset(new CaptionSaveStruct(*this, *m_pPam->GetPoint()));
4570 // Add a text section in the icon section as a container for the header
4571 // and set the PaM there
4572 const SwStartNode *pStNd;
4573 if (m_xTable.get() == pCurTable)
4574 pStNd = InsertTempTableCaptionSection();
4575 else
4576 pStNd = InsertTableSection( RES_POOLCOLL_TEXT );
4578 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::CAPTION_ON));
4580 // Table headers are always centered
4581 NewAttr(m_xAttrTab, &m_xAttrTab->pAdjust, SvxAdjustItem(SvxAdjust::Center, RES_PARATR_ADJUST));
4583 HTMLAttrs &rAttrs = xCntxt->GetAttrs();
4584 rAttrs.push_back( m_xAttrTab->pAdjust );
4586 PushContext(xCntxt);
4588 // Remember the start node of the section at the table
4589 pCurTable->SetCaption( pStNd, bTop );
4591 // If the first GetNextToken() doesn't succeed (pending input), must re-read from the beginning.
4592 SaveState( HtmlTokenId::NONE );
4595 if( nToken == HtmlTokenId::NONE )
4596 nToken = GetNextToken();
4598 // </CAPTION> is needed according to DTD
4599 bool bDone = false;
4600 while( IsParserWorking() && !bDone )
4602 SaveState( nToken );
4604 nToken = FilterToken( nToken );
4606 switch( nToken )
4608 case HtmlTokenId::TABLE_ON:
4609 if( m_vPendingStack.empty() )
4611 xSaveStruct->m_xTable = m_xTable;
4612 bool bHasToFly = xSaveStruct->m_xTable.get() != pCurTable;
4613 BuildTable( pCurTable->GetTableAdjust( true ),
4614 false, true, bHasToFly );
4616 else
4618 BuildTable( SvxAdjust::End );
4620 if( SvParserState::Pending != GetStatus() )
4622 m_xTable = xSaveStruct->m_xTable;
4624 break;
4625 case HtmlTokenId::TABLE_OFF:
4626 case HtmlTokenId::COLGROUP_ON:
4627 case HtmlTokenId::THEAD_ON:
4628 case HtmlTokenId::TFOOT_ON:
4629 case HtmlTokenId::TBODY_ON:
4630 case HtmlTokenId::TABLEROW_ON:
4631 SkipToken();
4632 bDone = true;
4633 break;
4635 case HtmlTokenId::CAPTION_OFF:
4636 bDone = true;
4637 break;
4638 default:
4639 if( !m_vPendingStack.empty() )
4641 m_vPendingStack.pop_back();
4642 OSL_ENSURE( m_vPendingStack.empty(), "Further it can't go!" );
4645 if( IsParserWorking() )
4646 NextToken( nToken );
4647 break;
4650 if( IsParserWorking() )
4651 SaveState( HtmlTokenId::NONE );
4653 if( !bDone )
4654 nToken = GetNextToken();
4657 if( SvParserState::Pending==GetStatus() )
4659 m_vPendingStack.emplace_back( HtmlTokenId::CAPTION_ON );
4660 m_vPendingStack.back().pData = std::move(xSaveStruct);
4661 return;
4664 // end all still open contexts
4665 while( m_aContexts.size() > m_nContextStAttrMin+1 )
4667 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
4668 EndContext(xCntxt.get());
4671 bool bLFStripped = StripTrailingLF() > 0;
4673 if (m_xTable.get() == pCurTable)
4675 // On moving the caption later, the last paragraph isn't moved as well.
4676 // That means, there has to be an empty paragraph at the end of the section
4677 if( m_pPam->GetPoint()->GetContentIndex() || bLFStripped )
4678 AppendTextNode( AM_NOSPACE );
4680 else
4682 // Strip LFs at the end of the paragraph
4683 if( !m_pPam->GetPoint()->GetContentIndex() && !bLFStripped )
4684 StripTrailingPara();
4687 // If there's an adjustment for the cell, we need to close it
4688 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
4689 if (xCntxt)
4691 EndContext(xCntxt.get());
4692 xCntxt.reset();
4695 SetAttr( false );
4697 // Recover stack and attribute table
4698 xSaveStruct->RestoreAll(*this);
4700 // Recover PaM
4701 *m_pPam->GetPoint() = xSaveStruct->GetPos();
4704 namespace {
4706 class TableSaveStruct : public SwPendingData
4708 public:
4709 std::shared_ptr<HTMLTable> m_xCurrentTable;
4711 explicit TableSaveStruct(std::shared_ptr<HTMLTable> xCurTable)
4712 : m_xCurrentTable(std::move(xCurTable))
4716 // Initiate creation of the table and put the table in a text frame if
4717 // needed. If it returns true, we need to insert a paragraph.
4718 void MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc );
4723 void TableSaveStruct::MakeTable( sal_uInt16 nWidth, SwPosition& rPos, SwDoc *pDoc )
4725 m_xCurrentTable->MakeTable(nullptr, nWidth);
4727 HTMLTableContext *pTCntxt = m_xCurrentTable->GetContext();
4728 OSL_ENSURE( pTCntxt, "Where is the table context" );
4730 SwTableNode *pTableNd = pTCntxt->GetTableNode();
4731 OSL_ENSURE( pTableNd, "Where is the table node" );
4733 if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && pTableNd )
4735 // If there's already a layout, the BoxFrames need to be regenerated at this table
4737 if( pTCntxt->GetFrameFormat() )
4739 pTCntxt->GetFrameFormat()->DelFrames();
4740 pTableNd->DelFrames();
4741 pTCntxt->GetFrameFormat()->MakeFrames();
4743 else
4745 pTableNd->DelFrames();
4746 SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
4747 OSL_ENSURE( aIdx.GetIndex() <= pTCntxt->GetPos()->GetNodeIndex(),
4748 "unexpected node for table layout" );
4749 pTableNd->MakeOwnFrames();
4753 rPos = *pTCntxt->GetPos();
4756 HTMLTableOptions::HTMLTableOptions( const HTMLOptions& rOptions,
4757 SvxAdjust eParentAdjust ) :
4758 nCols( 0 ),
4759 nWidth( 0 ), nHeight( 0 ),
4760 nCellPadding( USHRT_MAX ), nCellSpacing( USHRT_MAX ),
4761 nBorder( USHRT_MAX ),
4762 nHSpace( 0 ), nVSpace( 0 ),
4763 eAdjust( eParentAdjust ), eVertOri( text::VertOrientation::CENTER ),
4764 eFrame( HTMLTableFrame::Void ), eRules( HTMLTableRules::NONE ),
4765 bPercentWidth( false ),
4766 bTableAdjust( false ),
4767 bBGColor( false ),
4768 aBorderColor( COL_GRAY )
4770 bool bBorderColor = false;
4771 bool bHasFrame = false, bHasRules = false;
4773 for (size_t i = rOptions.size(); i; )
4775 const HTMLOption& rOption = rOptions[--i];
4776 switch( rOption.GetToken() )
4778 case HtmlOptionId::ID:
4779 aId = rOption.GetString();
4780 break;
4781 case HtmlOptionId::COLS:
4782 nCols = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4783 break;
4784 case HtmlOptionId::WIDTH:
4785 nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4786 bPercentWidth = (rOption.GetString().indexOf('%') != -1);
4787 if( bPercentWidth && nWidth>100 )
4788 nWidth = 100;
4789 break;
4790 case HtmlOptionId::HEIGHT:
4791 nHeight = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4792 if( rOption.GetString().indexOf('%') != -1 )
4793 nHeight = 0; // don't use % attributes
4794 break;
4795 case HtmlOptionId::CELLPADDING:
4796 nCellPadding = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4797 break;
4798 case HtmlOptionId::CELLSPACING:
4799 nCellSpacing = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4800 break;
4801 case HtmlOptionId::ALIGN:
4803 if( rOption.GetEnum( eAdjust, aHTMLPAlignTable ) )
4805 bTableAdjust = true;
4808 break;
4809 case HtmlOptionId::VALIGN:
4810 eVertOri = rOption.GetEnum( aHTMLTableVAlignTable, eVertOri );
4811 break;
4812 case HtmlOptionId::BORDER:
4813 // Handle BORDER and BORDER=BORDER like BORDER=1
4814 if (!rOption.GetString().isEmpty() &&
4815 !rOption.GetString().equalsIgnoreAsciiCase(
4816 OOO_STRING_SVTOOLS_HTML_O_border))
4818 nBorder = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4820 else
4821 nBorder = 1;
4823 if( !bHasFrame )
4824 eFrame = ( nBorder ? HTMLTableFrame::Box : HTMLTableFrame::Void );
4825 if( !bHasRules )
4826 eRules = ( nBorder ? HTMLTableRules::All : HTMLTableRules::NONE );
4827 break;
4828 case HtmlOptionId::FRAME:
4829 eFrame = rOption.GetTableFrame();
4830 bHasFrame = true;
4831 break;
4832 case HtmlOptionId::RULES:
4833 eRules = rOption.GetTableRules();
4834 bHasRules = true;
4835 break;
4836 case HtmlOptionId::BGCOLOR:
4837 // Ignore empty BGCOLOR on <TABLE>, <TR> and <TD>/<TH> like Netscape
4838 // *really* not on other tags
4839 if( !rOption.GetString().isEmpty() )
4841 rOption.GetColor( aBGColor );
4842 bBGColor = true;
4844 break;
4845 case HtmlOptionId::BACKGROUND:
4846 aBGImage = rOption.GetString();
4847 break;
4848 case HtmlOptionId::BORDERCOLOR:
4849 rOption.GetColor( aBorderColor );
4850 bBorderColor = true;
4851 break;
4852 case HtmlOptionId::BORDERCOLORDARK:
4853 if( !bBorderColor )
4854 rOption.GetColor( aBorderColor );
4855 break;
4856 case HtmlOptionId::STYLE:
4857 aStyle = rOption.GetString();
4858 break;
4859 case HtmlOptionId::CLASS:
4860 aClass = rOption.GetString();
4861 break;
4862 case HtmlOptionId::DIR:
4863 aDir = rOption.GetString();
4864 break;
4865 case HtmlOptionId::HSPACE:
4866 nHSpace = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4867 break;
4868 case HtmlOptionId::VSPACE:
4869 nVSpace = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
4870 break;
4871 default: break;
4875 if( nCols && !nWidth )
4877 nWidth = 100;
4878 bPercentWidth = true;
4881 // If BORDER=0 or no BORDER given, then there shouldn't be a border
4882 if( 0==nBorder || USHRT_MAX==nBorder )
4884 eFrame = HTMLTableFrame::Void;
4885 eRules = HTMLTableRules::NONE;
4889 void SwHTMLParser::DeleteSection(SwStartNode* pSttNd)
4891 //if section to be deleted contains a pending m_pMarquee, it will be deleted
4892 //so clear m_pMarquee pointer if that's the case
4893 SwFrameFormat* pObjectFormat = m_pMarquee ? ::FindFrameFormat(m_pMarquee.get()) : nullptr;
4894 FrameDeleteWatch aWatch(pObjectFormat);
4896 m_xDoc->getIDocumentContentOperations().DeleteSection(pSttNd);
4898 if (pObjectFormat)
4900 if (aWatch.WasDeleted())
4901 m_pMarquee = nullptr;
4902 else
4903 aWatch.EndListeningAll();
4907 std::shared_ptr<HTMLTable> SwHTMLParser::BuildTable(SvxAdjust eParentAdjust,
4908 bool bIsParentHead,
4909 bool bHasParentSection,
4910 bool bHasToFly)
4912 TableDepthGuard aGuard(*this);
4913 if (aGuard.TooDeep())
4914 eState = SvParserState::Error;
4916 if (!IsParserWorking() && m_vPendingStack.empty())
4917 return std::shared_ptr<HTMLTable>();
4919 ::comphelper::FlagRestorationGuard g(m_isInTableStructure, true);
4920 HtmlTokenId nToken = HtmlTokenId::NONE;
4921 bool bPending = false;
4922 std::unique_ptr<TableSaveStruct> xSaveStruct;
4924 if( !m_vPendingStack.empty() )
4926 xSaveStruct.reset(static_cast<TableSaveStruct*>(m_vPendingStack.back().pData.release()));
4928 m_vPendingStack.pop_back();
4929 nToken = !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : GetSaveToken();
4930 bPending = SvParserState::Error == eState && !m_vPendingStack.empty();
4932 SaveState( nToken );
4934 else
4936 m_xTable.reset();
4938 // Parse CSS on the table.
4939 OUString aStyle;
4940 const HTMLOptions& rHTMLOptions = GetOptions();
4941 for (size_t i = rHTMLOptions.size(); i;)
4943 const HTMLOption& rOption = rHTMLOptions[--i];
4944 if (rOption.GetToken() == HtmlOptionId::STYLE)
4946 aStyle = rOption.GetString();
4949 if (!aStyle.isEmpty())
4951 // Have inline CSS.
4952 SfxItemSet aItemSet(m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap());
4953 SvxCSS1PropertyInfo aPropInfo;
4954 if (ParseStyleOptions(aStyle, /*aId=*/OUString(), /*aClass=*/OUString(), aItemSet,
4955 aPropInfo))
4957 if (aPropInfo.m_eLeftMarginType == SVX_CSS1_LTYPE_AUTO
4958 && aPropInfo.m_eRightMarginType == SVX_CSS1_LTYPE_AUTO)
4960 // Both left & right is set to auto: that's our center.
4961 eParentAdjust = SvxAdjust::Center;
4966 HTMLTableOptions aTableOptions(GetOptions(), eParentAdjust);
4968 if (!aTableOptions.aId.isEmpty())
4969 InsertBookmark(aTableOptions.aId);
4971 std::shared_ptr<HTMLTable> xCurTable(std::make_shared<HTMLTable>(this,
4972 bIsParentHead,
4973 bHasParentSection,
4974 bHasToFly,
4975 aTableOptions));
4976 m_xTable = xCurTable;
4978 xSaveStruct.reset(new TableSaveStruct(xCurTable));
4980 // Is pending on the first GetNextToken, needs to be re-read on each construction
4981 SaveState( HtmlTokenId::NONE );
4984 std::shared_ptr<HTMLTable> xCurTable = xSaveStruct->m_xCurrentTable;
4986 // </TABLE> is needed according to DTD
4987 if( nToken == HtmlTokenId::NONE )
4988 nToken = GetNextToken();
4990 bool bDone = false;
4991 while( (IsParserWorking() && !bDone) || bPending )
4993 SaveState( nToken );
4995 nToken = FilterToken( nToken );
4997 OSL_ENSURE( !m_vPendingStack.empty() || !m_bCallNextToken ||
4998 xCurTable->GetContext() || xCurTable->HasParentSection(),
4999 "Where is the section?" );
5000 if( m_vPendingStack.empty() && m_bCallNextToken &&
5001 (xCurTable->GetContext() || xCurTable->HasParentSection()) )
5003 /// Call NextToken directly (e.g. ignore the content of floating frames or applets)
5004 NextToken( nToken );
5006 else switch( nToken )
5008 case HtmlTokenId::TABLE_ON:
5009 if( !xCurTable->GetContext() )
5011 // If there's no table added, read the next table'
5012 SkipToken();
5013 bDone = true;
5016 break;
5017 case HtmlTokenId::TABLE_OFF:
5018 bDone = true;
5019 break;
5020 case HtmlTokenId::CAPTION_ON:
5021 BuildTableCaption(xCurTable.get());
5022 bDone = m_xTable->IsOverflowing();
5023 break;
5024 case HtmlTokenId::COL_ON:
5025 SkipToken();
5026 BuildTableColGroup(xCurTable.get(), false);
5027 break;
5028 case HtmlTokenId::COLGROUP_ON:
5029 BuildTableColGroup(xCurTable.get(), true);
5030 break;
5031 case HtmlTokenId::TABLEROW_ON:
5032 case HtmlTokenId::TABLEHEADER_ON:
5033 case HtmlTokenId::TABLEDATA_ON:
5034 SkipToken();
5035 BuildTableSection(xCurTable.get(), false, false);
5036 bDone = m_xTable->IsOverflowing();
5037 break;
5038 case HtmlTokenId::THEAD_ON:
5039 case HtmlTokenId::TFOOT_ON:
5040 case HtmlTokenId::TBODY_ON:
5041 BuildTableSection(xCurTable.get(), true, HtmlTokenId::THEAD_ON==nToken);
5042 bDone = m_xTable->IsOverflowing();
5043 break;
5044 case HtmlTokenId::MULTICOL_ON:
5045 // we can't add columned text frames here
5046 break;
5047 case HtmlTokenId::FORM_ON:
5048 NewForm( false ); // don't add a new paragraph
5049 break;
5050 case HtmlTokenId::FORM_OFF:
5051 EndForm( false ); // don't add a new paragraph
5052 break;
5053 case HtmlTokenId::TEXTTOKEN:
5054 // blank strings may be a series of CR+LF and no text
5055 if( (xCurTable->GetContext() ||
5056 !xCurTable->HasParentSection()) &&
5057 1==aToken.getLength() && ' '==aToken[0] )
5058 break;
5059 [[fallthrough]];
5060 default:
5061 xCurTable->MakeParentContents();
5062 NextToken( nToken );
5063 break;
5066 OSL_ENSURE( !bPending || m_vPendingStack.empty(),
5067 "SwHTMLParser::BuildTable: There is a PendStack again" );
5068 bPending = false;
5069 if( IsParserWorking() )
5070 SaveState( HtmlTokenId::NONE );
5072 if( !bDone )
5073 nToken = GetNextToken();
5076 if( SvParserState::Pending == GetStatus() )
5078 m_vPendingStack.emplace_back( HtmlTokenId::TABLE_ON );
5079 m_vPendingStack.back().pData = std::move(xSaveStruct);
5080 return std::shared_ptr<HTMLTable>();
5083 HTMLTableContext *pTCntxt = xCurTable->GetContext();
5084 if( pTCntxt )
5087 // Modify table structure
5088 xCurTable->CloseTable();
5090 // end contexts that began out of cells. Needs to exist before (!) we move the table,
5091 // since the current one doesn't exist anymore afterwards
5092 while( m_aContexts.size() > m_nContextStAttrMin )
5094 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
5095 if (!xCntxt)
5096 break;
5097 ClearContext(xCntxt.get());
5100 m_nContextStMin = pTCntxt->GetContextStMin();
5101 m_nContextStAttrMin = pTCntxt->GetContextStAttrMin();
5103 if (m_xTable == xCurTable)
5105 // Set table caption
5106 const SwStartNode *pCapStNd = m_xTable->GetCaptionStartNode();
5107 if( pCapStNd )
5109 // The last paragraph of the section is never part of the copy.
5110 // That's why the section needs to contain at least two paragraphs
5112 if( pCapStNd->EndOfSectionIndex() - pCapStNd->GetIndex() > SwNodeOffset(2) )
5114 // Don't copy start node and the last paragraph
5115 SwNodeRange aSrcRg( *pCapStNd, SwNodeOffset(1),
5116 *pCapStNd->EndOfSectionNode(), SwNodeOffset(-1) );
5118 bool bTop = m_xTable->IsTopCaption();
5119 SwStartNode *pTableStNd = pTCntxt->GetTableNode();
5121 OSL_ENSURE( pTableStNd, "Where is the table node" );
5122 OSL_ENSURE( pTableStNd == m_pPam->GetPointNode().FindTableNode(),
5123 "Are we in the wrong table?" );
5125 if (pTableStNd)
5127 SwNode* pNd;
5128 if( bTop )
5129 pNd = pTableStNd;
5130 else
5131 pNd = pTableStNd->EndOfSectionNode();
5132 SwNodeIndex aDstIdx( *pNd, bTop ? 0 : 1 );
5134 m_xDoc->getIDocumentContentOperations().MoveNodeRange( aSrcRg, aDstIdx.GetNode(),
5135 SwMoveFlags::DEFAULT );
5137 // If the caption was added before the table, a page style on that table
5138 // needs to be moved to the first paragraph of the header.
5139 // Additionally, all remembered indices that point to the table node
5140 // need to be moved
5141 if( bTop )
5143 MovePageDescAttrs( pTableStNd, aSrcRg.aStart.GetIndex(),
5144 false );
5149 // The section isn't needed anymore
5150 m_pPam->SetMark();
5151 m_pPam->DeleteMark();
5152 DeleteSection(const_cast<SwStartNode*>(pCapStNd));
5153 m_xTable->SetCaption( nullptr, false );
5156 // Process SwTable
5157 sal_uInt16 nBrowseWidth = o3tl::narrowing<sal_uInt16>(GetCurrentBrowseWidth());
5158 xSaveStruct->MakeTable(nBrowseWidth, *m_pPam->GetPoint(), m_xDoc.get());
5161 GetNumInfo().Set( pTCntxt->GetNumInfo() );
5162 pTCntxt->RestorePREListingXMP( *this );
5163 RestoreAttrTab(pTCntxt->m_xAttrTab);
5165 if (m_xTable == xCurTable)
5167 // Set upper paragraph spacing
5168 m_bUpperSpace = true;
5169 SetTextCollAttrs();
5171 SwTableNode* pTableNode = pTCntxt->GetTableNode();
5172 size_t nTableBoxSize = pTableNode ? pTableNode->GetTable().GetTabSortBoxes().size() : 0;
5173 m_nParaCnt = m_nParaCnt - std::min(m_nParaCnt, nTableBoxSize);
5175 // Jump to a table if needed
5176 if( JumpToMarks::Table == m_eJumpTo && m_xTable->GetSwTable() &&
5177 m_xTable->GetSwTable()->GetFrameFormat()->GetName() == m_sJmpMark )
5179 m_bChkJumpMark = true;
5180 m_eJumpTo = JumpToMarks::NONE;
5183 // If the import was canceled, don't call Show again here since
5184 // the SwViewShell was already deleted
5185 // That's not enough. Even in the ACCEPTING_STATE, a Show mustn't be called
5186 // because otherwise the parser's gonna be destroyed on the reschedule,
5187 // if there's still a DataAvailable link coming. So: only in the WORKING state
5188 if( !m_nParaCnt && SvParserState::Working == GetStatus() )
5189 Show();
5192 else if (m_xTable == xCurTable)
5194 // There was no table read
5196 // We maybe need to delete a read caption
5197 const SwStartNode *pCapStNd = xCurTable->GetCaptionStartNode();
5198 if( pCapStNd )
5200 m_pPam->SetMark();
5201 m_pPam->DeleteMark();
5202 DeleteSection(const_cast<SwStartNode*>(pCapStNd));
5203 xCurTable->SetCaption( nullptr, false );
5207 if (m_xTable == xCurTable)
5209 xSaveStruct->m_xCurrentTable.reset();
5210 m_xTable.reset();
5213 std::shared_ptr<HTMLTable> xRetTable = xSaveStruct->m_xCurrentTable;
5214 xSaveStruct.reset();
5216 return xRetTable;
5219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */