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