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