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