LibreOffice Module sc (master)  1
htmlpars.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <sal/config.h>
22 
23 #include <comphelper/string.hxx>
24 
25 #include <scitems.hxx>
26 
27 #include <svtools/htmlcfg.hxx>
28 #include <editeng/colritem.hxx>
29 #include <editeng/brushitem.hxx>
30 #include <editeng/editeng.hxx>
31 #include <editeng/fhgtitem.hxx>
32 #include <editeng/fontitem.hxx>
33 #include <editeng/postitem.hxx>
34 #include <editeng/udlnitem.hxx>
35 #include <editeng/wghtitem.hxx>
36 #include <editeng/borderline.hxx>
37 #include <editeng/boxitem.hxx>
38 #include <editeng/justifyitem.hxx>
39 #include <sfx2/objsh.hxx>
40 #include <svl/intitem.hxx>
41 #include <vcl/graphicfilter.hxx>
42 #include <svtools/parhtml.hxx>
43 #include <svtools/htmlkywd.hxx>
44 #include <svtools/htmltokn.h>
45 
46 #include <vcl/outdev.hxx>
47 #include <vcl/svapp.hxx>
48 #include <tools/urlobj.hxx>
49 #include <osl/diagnose.h>
50 
51 #include <rtl/tencinfo.h>
52 
53 #include <attrib.hxx>
54 #include <htmlpars.hxx>
55 #include <global.hxx>
56 #include <document.hxx>
57 #include <rangelst.hxx>
58 
59 #include <orcus/css_parser.hpp>
60 
61 #include <com/sun/star/document/XDocumentProperties.hpp>
62 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
63 #include <com/sun/star/frame/XModel.hpp>
64 #include <numeric>
65 #include <utility>
66 
67 using ::editeng::SvxBorderLine;
68 using namespace ::com::sun::star;
69 
71 
72 void ScHTMLStyles::add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
73  const OUString& aProp, const OUString& aValue)
74 {
75  if (pElemName)
76  {
77  OUString aElem(pElemName, nElemName, RTL_TEXTENCODING_UTF8);
78  aElem = aElem.toAsciiLowerCase();
79  if (pClassName)
80  {
81  // Both element and class names given.
82  ElemsType::iterator itrElem = m_ElemProps.find(aElem);
83  if (itrElem == m_ElemProps.end())
84  {
85  // new element
86  std::pair<ElemsType::iterator, bool> r =
87  m_ElemProps.insert(std::make_pair(aElem, std::make_unique<NamePropsType>()));
88  if (!r.second)
89  // insertion failed.
90  return;
91  itrElem = r.first;
92  }
93 
94  NamePropsType *const pClsProps = itrElem->second.get();
95  OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
96  aClass = aClass.toAsciiLowerCase();
97  insertProp(*pClsProps, aClass, aProp, aValue);
98  }
99  else
100  {
101  // Element name only. Add it to the element global.
102  insertProp(m_ElemGlobalProps, aElem, aProp, aValue);
103  }
104  }
105  else
106  {
107  if (pClassName)
108  {
109  // Class name only. Add it to the global.
110  OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
111  aClass = aClass.toAsciiLowerCase();
112  insertProp(m_GlobalProps, aClass, aProp, aValue);
113  }
114  }
115 }
116 
118  const OUString& rElem, const OUString& rClass, const OUString& rPropName) const
119 {
120  // First, look into the element-class storage.
121  {
122  auto const itr = m_ElemProps.find(rElem);
123  if (itr != m_ElemProps.end())
124  {
125  const NamePropsType *const pClasses = itr->second.get();
126  NamePropsType::const_iterator itr2 = pClasses->find(rClass);
127  if (itr2 != pClasses->end())
128  {
129  const PropsType *const pProps = itr2->second.get();
130  PropsType::const_iterator itr3 = pProps->find(rPropName);
131  if (itr3 != pProps->end())
132  return itr3->second;
133  }
134  }
135  }
136  // Next, look into the class global storage.
137  {
138  auto const itr = m_GlobalProps.find(rClass);
139  if (itr != m_GlobalProps.end())
140  {
141  const PropsType *const pProps = itr->second.get();
142  PropsType::const_iterator itr2 = pProps->find(rPropName);
143  if (itr2 != pProps->end())
144  return itr2->second;
145  }
146  }
147  // As the last resort, look into the element global storage.
148  {
149  auto const itr = m_ElemGlobalProps.find(rClass);
150  if (itr != m_ElemGlobalProps.end())
151  {
152  const PropsType *const pProps = itr->second.get();
153  PropsType::const_iterator itr2 = pProps->find(rPropName);
154  if (itr2 != pProps->end())
155  return itr2->second;
156  }
157  }
158 
159  return maEmpty; // nothing found.
160 }
161 
163  NamePropsType& rStore, const OUString& aName,
164  const OUString& aProp, const OUString& aValue)
165 {
166  NamePropsType::iterator itr = rStore.find(aName);
167  if (itr == rStore.end())
168  {
169  // new element
170  std::pair<NamePropsType::iterator, bool> r =
171  rStore.insert(std::make_pair(aName, std::make_unique<PropsType>()));
172  if (!r.second)
173  // insertion failed.
174  return;
175 
176  itr = r.first;
177  }
178 
179  PropsType *const pProps = itr->second.get();
180  pProps->emplace(aProp, aValue);
181 }
182 
183 // BASE class for HTML parser classes
184 
186  ScEEParser( pEditEngine ),
187  mpDoc( pDoc )
188 {
189  SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
190  for( sal_uInt16 nIndex = 0; nIndex < SC_HTML_FONTSIZES; ++nIndex )
191  maFontHeights[ nIndex ] = rHtmlOptions.GetFontSize( nIndex ) * 20;
192 }
193 
195 {
196 }
197 
199  EditEngine* pEditP, const OUString& rBaseURL, const Size& aPageSizeP,
200  ScDocument* pDocP ) :
201  ScHTMLParser( pEditP, pDocP ),
202  aPageSize( aPageSizeP ),
203  aBaseURL( rBaseURL ),
204  xLockedList( new ScRangeList ),
205  pLocalColOffset( new ScHTMLColOffset ),
206  nFirstTableCell(0),
207  nTableLevel(0),
208  nTable(0),
209  nMaxTable(0),
210  nColCntStart(0),
211  nMaxCol(0),
212  nTableWidth(0),
213  nColOffset(0),
214  nColOffsetStart(0),
215  nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
216  bFirstRow( true ),
217  bTabInTabCell( false ),
218  bInCell( false ),
219  bInTitle( false )
220 {
221  MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
222  MakeColNoRef( &maColOffset, 0, 0, 0, 0 );
223 }
224 
226 {
227  while ( !aTableStack.empty() )
228  {
229  ScHTMLTableStackEntry * pS = aTableStack.top().get();
230  if ( pS->pLocalColOffset != pLocalColOffset )
231  delete pS->pLocalColOffset;
232  aTableStack.pop();
233  }
234  delete pLocalColOffset;
235  if ( pTables )
236  {
237  for( const auto& rEntry : *pTables)
238  delete rEntry.second;
239  pTables.reset();
240  }
241 }
242 
243 ErrCode ScHTMLLayoutParser::Read( SvStream& rStream, const OUString& rBaseURL )
244 {
246  pEdit->SetHtmlImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
247 
249  bool bLoading = pObjSh && pObjSh->IsLoading();
250 
251  SvKeyValueIteratorRef xValues;
252  SvKeyValueIterator* pAttributes = nullptr;
253  if ( bLoading )
254  pAttributes = pObjSh->GetHeaderAttributes();
255  else
256  {
257  // When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
258  // (used when pasting from clipboard)
259  const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
260  if( pCharSet )
261  {
262  OUString aContentType = "text/html; charset=" +
263  OUString::createFromAscii( pCharSet );
264 
265  xValues = new SvKeyValueIterator;
266  xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
267  pAttributes = xValues.get();
268  }
269  }
270 
271  ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Html, pAttributes );
272 
273  pEdit->SetHtmlImportHdl( aOldLink );
274  // Create column width
275  Adjust();
277  sal_uInt16 nCount = maColOffset.size();
278  sal_uLong nOff = maColOffset[0];
279  Size aSize;
280  for ( sal_uInt16 j = 1; j < nCount; j++ )
281  {
282  aSize.setWidth( maColOffset[j] - nOff );
283  aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MapUnit::MapTwip ) );
284  maColWidths[ j-1 ] = aSize.Width();
285  nOff = maColOffset[j];
286  }
287  return nErr;
288 }
289 
291 {
292  return nullptr;
293 }
294 
296 {
298  if ( pE )
299  {
300  if ( !pE->aSel.HasRange() )
301  { // Completely empty, following text ends up in the same paragraph!
302  mxActEntry->aSel.nStartPara = pE->aSel.nEndPara;
303  mxActEntry->aSel.nStartPos = pE->aSel.nEndPos;
304  }
305  }
306  mxActEntry->aSel.nEndPara = mxActEntry->aSel.nStartPara;
307  mxActEntry->aSel.nEndPos = mxActEntry->aSel.nStartPos;
308 }
309 
311 {
312  if ( rSel.nEndPara >= pE->aSel.nStartPara )
313  {
314  pE->aSel.nEndPara = rSel.nEndPara;
315  pE->aSel.nEndPos = rSel.nEndPos;
316  }
317  else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
318  { // Did not attach a paragraph, but empty, do nothing
319  }
320  else
321  {
322  OSL_FAIL( "EntryEnd: EditEngine ESelection End < Start" );
323  }
324 }
325 
327 {
328  if ( bInCell )
329  CloseEntry( pInfo );
330  if ( nRowMax < ++nRowCnt )
331  nRowMax = nRowCnt;
334  bFirstRow = false;
335 }
336 
337 bool ScHTMLLayoutParser::SeekOffset( const ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
338  SCCOL* pCol, sal_uInt16 nOffsetTol )
339 {
340  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
341  ScHTMLColOffset::const_iterator it = pOffset->find( nOffset );
342  bool bFound = it != pOffset->end();
343  sal_uInt16 nPos = it - pOffset->begin();
344  *pCol = static_cast<SCCOL>(nPos);
345  if ( bFound )
346  return true;
347  sal_uInt16 nCount = pOffset->size();
348  if ( !nCount )
349  return false;
350  // nPos is the position of insertion, that's where the next higher one is (or isn't)
351  if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
352  return true;
353  // Not smaller than everything else? Then compare with the next lower one
354  else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
355  {
356  (*pCol)--;
357  return true;
358  }
359  return false;
360 }
361 
362 void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
363  sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
364 {
365  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
366  SCCOL nPos;
367  if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
368  nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
369  else
370  pOffset->insert( nOffset );
371  if ( nWidth )
372  {
373  if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
374  nWidth = static_cast<sal_uInt16>((*pOffset)[nPos]) - nOffset;
375  else
376  pOffset->insert( nOffset + nWidth );
377  }
378 }
379 
380 void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
381  sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
382 {
383  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
384  SCCOL nPos;
385  if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
386  nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
387  else
388  pOffset->insert( nOffset );
389  if ( nWidth )
390  {
391  if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
392  pOffset->insert( nOffset + nWidth );
393  }
394 }
395 
396 void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
397  sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
398 {
399  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
400  SCCOL nPos;
401  if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
402  {
403  if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
404  nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
405  else
406  pOffset->insert( nNewOffset );
407  return ;
408  }
409  nOldOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
410  SCCOL nPos2;
411  if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
412  {
413  nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos2]);
414  return ;
415  }
416  tools::Long nDiff = nNewOffset - nOldOffset;
417  if ( nDiff < 0 )
418  {
419  do
420  {
421  const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
422  } while ( nPos-- );
423  }
424  else
425  {
426  do
427  {
428  const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
429  } while ( ++nPos < static_cast<sal_uInt16>(pOffset->size()) );
430  }
431 }
432 
434 {
435  if ( !mpDoc->ValidCol(pE->nCol) )
436  return;
437 
438 // Or else this would create a wrong value at ScAddress (chance for an infinite loop)!
439  bool bBadCol = false;
440  bool bAgain;
441  ScRange aRange( pE->nCol, pE->nRow, 0,
442  pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
443  do
444  {
445  bAgain = false;
446  for ( size_t i = 0, nRanges = xLockedList->size(); i < nRanges; ++i )
447  {
448  ScRange & rR = (*xLockedList)[i];
449  if ( rR.Intersects( aRange ) )
450  {
451  pE->nCol = rR.aEnd.Col() + 1;
452  SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
453  if ( pE->nCol > mpDoc->MaxCol() || nTmp > mpDoc->MaxCol() )
454  bBadCol = true;
455  else
456  {
457  bAgain = true;
458  aRange.aStart.SetCol( pE->nCol );
459  aRange.aEnd.SetCol( nTmp );
460  }
461  break;
462  }
463  }
464  } while ( bAgain );
465  if ( bJoin && !bBadCol )
466  xLockedList->Join( aRange );
467 }
468 
470 {
472 
473  std::stack< std::unique_ptr<ScHTMLAdjustStackEntry> > aStack;
474  sal_uInt16 nTab = 0;
475  SCCOL nLastCol = SCCOL_MAX;
476  SCROW nNextRow = 0;
477  SCROW nCurRow = 0;
478  sal_uInt16 nPageWidth = static_cast<sal_uInt16>(aPageSize.Width());
479  InnerMap* pTab = nullptr;
480  for (auto& pE : maList)
481  {
482  if ( pE->nTab < nTab )
483  { // Table finished
484  if ( !aStack.empty() )
485  {
486  std::unique_ptr<ScHTMLAdjustStackEntry> pS = std::move(aStack.top());
487  aStack.pop();
488 
489  nLastCol = pS->nLastCol;
490  nNextRow = pS->nNextRow;
491  nCurRow = pS->nCurRow;
492  }
493  nTab = pE->nTab;
494  if (pTables)
495  {
496  OuterMap::const_iterator it = pTables->find( nTab );
497  if ( it != pTables->end() )
498  pTab = it->second;
499  }
500 
501  }
502  SCROW nRow = pE->nRow;
503  if ( pE->nCol <= nLastCol )
504  { // Next row
505  if ( pE->nRow < nNextRow )
506  pE->nRow = nCurRow = nNextRow;
507  else
508  nCurRow = nNextRow = pE->nRow;
509  SCROW nR = 0;
510  if ( pTab )
511  {
512  InnerMap::const_iterator it = pTab->find( nCurRow );
513  if ( it != pTab->end() )
514  nR = it->second;
515  }
516  if ( nR )
517  nNextRow += nR;
518  else
519  nNextRow++;
520  }
521  else
522  pE->nRow = nCurRow;
523  nLastCol = pE->nCol; // Read column
524  if ( pE->nTab > nTab )
525  { // New table
526  aStack.push( std::make_unique<ScHTMLAdjustStackEntry>(
527  nLastCol, nNextRow, nCurRow ) );
528  nTab = pE->nTab;
529  if ( pTables )
530  {
531  OuterMap::const_iterator it = pTables->find( nTab );
532  if ( it != pTables->end() )
533  pTab = it->second;
534  }
535  // New line spacing
536  SCROW nR = 0;
537  if ( pTab )
538  {
539  InnerMap::const_iterator it = pTab->find( nCurRow );
540  if ( it != pTab->end() )
541  nR = it->second;
542  }
543  if ( nR )
544  nNextRow = nCurRow + nR;
545  else
546  nNextRow = nCurRow + 1;
547  }
548  if ( nTab == 0 )
549  pE->nWidth = nPageWidth;
550  else
551  { // Real table, no paragraphs on the field
552  if ( pTab )
553  {
554  SCROW nRowSpan = pE->nRowOverlap;
555  for ( SCROW j=0; j < nRowSpan; j++ )
556  { // RowSpan resulting from merged rows
557  SCROW nRows = 0;
558  InnerMap::const_iterator it = pTab->find( nRow+j );
559  if ( it != pTab->end() )
560  nRows = it->second;
561  if ( nRows > 1 )
562  {
563  pE->nRowOverlap += nRows - 1;
564  if ( j == 0 )
565  { // Merged rows move the next row
566  SCROW nTmp = nCurRow + nRows;
567  if ( nNextRow < nTmp )
568  nNextRow = nTmp;
569  }
570  }
571  }
572  }
573  }
574  // Real column
575  (void)SeekOffset( &maColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
576  SCCOL nColBeforeSkip = pE->nCol;
577  SkipLocked(pE.get(), false);
578  if ( pE->nCol != nColBeforeSkip )
579  {
580  SCCOL nCount = static_cast<SCCOL>(maColOffset.size());
581  if ( nCount <= pE->nCol )
582  {
583  pE->nOffset = static_cast<sal_uInt16>(maColOffset[nCount-1]);
584  MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
585  }
586  else
587  {
588  pE->nOffset = static_cast<sal_uInt16>(maColOffset[pE->nCol]);
589  }
590  }
591  SCCOL nPos;
592  if ( pE->nWidth && SeekOffset( &maColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
593  pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
594  else
595  {
596  //FIXME: This may not be correct, but works anyway ...
597  pE->nColOverlap = 1;
598  }
599  xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
600  pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
601  // Take over MaxDimensions
602  SCCOL nColTmp = pE->nCol + pE->nColOverlap;
603  if ( nColMax < nColTmp )
604  nColMax = nColTmp;
605  SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
606  if ( nRowMax < nRowTmp )
607  nRowMax = nRowTmp;
608  }
609 }
610 
612 {
613  if ( pE->nWidth )
614  return pE->nWidth;
615  sal_Int32 nTmp = std::min( static_cast<sal_Int32>( pE->nCol -
616  nColCntStart + pE->nColOverlap),
617  static_cast<sal_Int32>( pLocalColOffset->size() - 1));
618  SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
619  sal_uInt16 nOff2 = static_cast<sal_uInt16>((*pLocalColOffset)[nPos]);
620  if ( pE->nOffset < nOff2 )
621  return nOff2 - pE->nOffset;
622  return 0;
623 }
624 
626 {
627  SCCOL nCol;
628  if ( !nTableWidth )
629  nTableWidth = static_cast<sal_uInt16>(aPageSize.Width());
630  SCCOL nColsPerRow = nMaxCol - nColCntStart;
631  if ( nColsPerRow <= 0 )
632  nColsPerRow = 1;
633  if ( pLocalColOffset->size() <= 2 )
634  { // Only PageSize, there was no width setting
635  sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
636  sal_uInt16 nOff = nColOffsetStart;
638  for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
639  {
640  MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
641  }
642  nTableWidth = static_cast<sal_uInt16>(pLocalColOffset->back() - pLocalColOffset->front());
643  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
644  {
645  auto& pE = maList[ i ];
646  if ( pE->nTab == nTable )
647  {
648  pE->nOffset = static_cast<sal_uInt16>((*pLocalColOffset)[pE->nCol - nColCntStart]);
649  pE->nWidth = 0; // to be recalculated later
650  }
651  }
652  }
653  else
654  { // Some without width
655  // Why actually no pE?
656  if ( nFirstTableCell < maList.size() )
657  {
658  std::unique_ptr<sal_uInt16[]> pOffsets(new sal_uInt16[ nColsPerRow+1 ]);
659  memset( pOffsets.get(), 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
660  std::unique_ptr<sal_uInt16[]> pWidths(new sal_uInt16[ nColsPerRow ]);
661  memset( pWidths.get(), 0, nColsPerRow * sizeof(sal_uInt16) );
662  pOffsets[0] = nColOffsetStart;
663  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
664  {
665  auto& pE = maList[ i ];
666  if ( pE->nTab == nTable && pE->nWidth )
667  {
668  nCol = pE->nCol - nColCntStart;
669  if ( nCol < nColsPerRow )
670  {
671  if ( pE->nColOverlap == 1 )
672  {
673  if ( pWidths[nCol] < pE->nWidth )
674  pWidths[nCol] = pE->nWidth;
675  }
676  else
677  { // try to find a single undefined width
678  sal_uInt16 nTotal = 0;
679  bool bFound = false;
680  SCCOL nHere = 0;
681  SCCOL nStop = std::min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
682  for ( ; nCol < nStop; nCol++ )
683  {
684  if ( pWidths[nCol] )
685  nTotal = nTotal + pWidths[nCol];
686  else
687  {
688  if ( bFound )
689  {
690  bFound = false;
691  break; // for
692  }
693  bFound = true;
694  nHere = nCol;
695  }
696  }
697  if ( bFound && pE->nWidth > nTotal )
698  pWidths[nHere] = pE->nWidth - nTotal;
699  }
700  }
701  }
702  }
703  sal_uInt16 nWidths = 0;
704  sal_uInt16 nUnknown = 0;
705  for ( nCol = 0; nCol < nColsPerRow; nCol++ )
706  {
707  if ( pWidths[nCol] )
708  nWidths = nWidths + pWidths[nCol];
709  else
710  nUnknown++;
711  }
712  if ( nUnknown )
713  {
714  sal_uInt16 nW = ((nWidths < nTableWidth) ?
715  ((nTableWidth - nWidths) / nUnknown) :
716  (nTableWidth / nUnknown));
717  for ( nCol = 0; nCol < nColsPerRow; nCol++ )
718  {
719  if ( !pWidths[nCol] )
720  pWidths[nCol] = nW;
721  }
722  }
723  for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
724  {
725  pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
726  }
728  for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
729  {
730  MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
731  }
732  nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
733 
734  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
735  {
736  auto& pE = maList[ i ];
737  if ( pE->nTab == nTable )
738  {
739  nCol = pE->nCol - nColCntStart;
740  OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
741  if ( nCol < nColsPerRow )
742  {
743  pE->nOffset = pOffsets[nCol];
744  nCol = nCol + pE->nColOverlap;
745  if ( nCol > nColsPerRow )
746  nCol = nColsPerRow;
747  pE->nWidth = pOffsets[nCol] - pE->nOffset;
748  }
749  }
750  }
751  }
752  }
753  if ( !pLocalColOffset->empty() )
754  {
755  sal_uInt16 nMax = static_cast<sal_uInt16>(pLocalColOffset->back());
756  if ( aPageSize.Width() < nMax )
757  aPageSize.setWidth( nMax );
758  }
759  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
760  {
761  auto& pE = maList[ i ];
762  if ( pE->nTab == nTable )
763  {
764  if ( !pE->nWidth )
765  {
766  pE->nWidth = GetWidth(pE.get());
767  OSL_ENSURE( pE->nWidth, "SetWidths: pE->nWidth == 0" );
768  }
769  MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
770  }
771  }
772 }
773 
775 {
776  if ( pE->nCol == SCCOL_MAX )
777  pE->nCol = nColCnt;
778  if ( pE->nRow == SCROW_MAX )
779  pE->nRow = nRowCnt;
780  SCCOL nCol = pE->nCol;
781  SkipLocked( pE ); // Change of columns to the right
782 
783  if ( nCol < pE->nCol )
784  { // Replaced
785  nCol = pE->nCol - nColCntStart;
786  SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size());
787  if ( nCol < nCount )
788  nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCol]);
789  else
790  nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCount - 1]);
791  }
792  pE->nOffset = nColOffset;
793  sal_uInt16 nWidth = GetWidth( pE );
795  if ( pE->nWidth )
796  pE->nWidth = nWidth;
797  nColOffset = pE->nOffset + nWidth;
800 }
801 
803 {
804  bInCell = false;
805  if ( bTabInTabCell )
806  { // From the stack in TableOff
807  bTabInTabCell = false;
808  NewActEntry(maList.back().get()); // New free flying mxActEntry
809  return ;
810  }
811  if (mxActEntry->nTab == 0)
812  mxActEntry->nWidth = static_cast<sal_uInt16>(aPageSize.Width());
813  Colonize(mxActEntry.get());
814  nColCnt = mxActEntry->nCol + mxActEntry->nColOverlap;
815  if ( nMaxCol < nColCnt )
816  nMaxCol = nColCnt; // TableStack MaxCol
817  if ( nColMax < nColCnt )
818  nColMax = nColCnt; // Global MaxCol for ScEEParser GetDimensions!
819  EntryEnd(mxActEntry.get(), pInfo->aSelection);
820  ESelection& rSel = mxActEntry->aSel;
821  while ( rSel.nStartPara < rSel.nEndPara
822  && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
823  { // Strip preceding empty paragraphs
824  rSel.nStartPara++;
825  }
826  while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
827  { // Strip successive empty paragraphs
828  rSel.nEndPara--;
829  rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
830  }
831  if ( rSel.nStartPara > rSel.nEndPara )
832  { // Gives GPF in CreateTextObject
833  OSL_FAIL( "CloseEntry: EditEngine ESelection Start > End" );
834  rSel.nEndPara = rSel.nStartPara;
835  }
836  if ( rSel.HasRange() )
837  mxActEntry->aItemSet.Put( ScLineBreakCell(true) );
838  maList.push_back(mxActEntry);
839  NewActEntry(mxActEntry.get()); // New free flying mxActEntry
840 }
841 
842 IMPL_LINK( ScHTMLLayoutParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
843 {
844  switch ( rInfo.eState )
845  {
846  case HtmlImportState::NextToken:
847  ProcToken( &rInfo );
848  break;
849  case HtmlImportState::Start:
850  break;
851  case HtmlImportState::End:
852  if ( rInfo.aSelection.nEndPos )
853  {
854  // If text remains: create paragraph, without calling CloseEntry().
855  if( bInCell ) // ...but only in opened table cells.
856  {
857  bInCell = false;
858  NextRow( &rInfo );
859  bInCell = true;
860  }
861  CloseEntry( &rInfo );
862  }
863  while ( nTableLevel > 0 )
864  TableOff( &rInfo ); // close tables, if </TABLE> missing
865  break;
866  case HtmlImportState::SetAttr:
867  break;
868  case HtmlImportState::InsertText:
869  break;
870  case HtmlImportState::InsertPara:
871  if ( nTableLevel < 1 )
872  {
873  CloseEntry( &rInfo );
874  NextRow( &rInfo );
875  }
876  break;
877  case HtmlImportState::InsertField:
878  break;
879  default:
880  OSL_FAIL("HTMLImportHdl: unknown ImportInfo.eState");
881  }
882 }
883 
885 {
886  if ( bInCell )
887  CloseEntry( pInfo );
888  if ( !nTableLevel )
889  {
890  OSL_FAIL( "dumbo doc! <TH> or <TD> without previous <TABLE>" );
891  TableOn( pInfo );
892  }
893  bInCell = true;
894  bool bHorJustifyCenterTH = (pInfo->nToken == HtmlTokenId::TABLEHEADER_ON);
895  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
896  for (const auto & rOption : rOptions)
897  {
898  switch( rOption.GetToken() )
899  {
900  case HtmlOptionId::COLSPAN:
901  {
902  mxActEntry->nColOverlap = static_cast<SCCOL>(rOption.GetString().toInt32());
903  }
904  break;
905  case HtmlOptionId::ROWSPAN:
906  {
907  mxActEntry->nRowOverlap = static_cast<SCROW>(rOption.GetString().toInt32());
908  }
909  break;
910  case HtmlOptionId::ALIGN:
911  {
912  bHorJustifyCenterTH = false;
913  SvxCellHorJustify eVal;
914  const OUString& rOptVal = rOption.GetString();
915  if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
916  eVal = SvxCellHorJustify::Right;
917  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
918  eVal = SvxCellHorJustify::Center;
919  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
920  eVal = SvxCellHorJustify::Left;
921  else
922  eVal = SvxCellHorJustify::Standard;
923  if ( eVal != SvxCellHorJustify::Standard )
924  mxActEntry->aItemSet.Put(SvxHorJustifyItem(eVal, ATTR_HOR_JUSTIFY));
925  }
926  break;
927  case HtmlOptionId::VALIGN:
928  {
929  SvxCellVerJustify eVal;
930  const OUString& rOptVal = rOption.GetString();
931  if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
932  eVal = SvxCellVerJustify::Top;
933  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
934  eVal = SvxCellVerJustify::Center;
935  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
936  eVal = SvxCellVerJustify::Bottom;
937  else
938  eVal = SvxCellVerJustify::Standard;
939  mxActEntry->aItemSet.Put(SvxVerJustifyItem(eVal, ATTR_VER_JUSTIFY));
940  }
941  break;
942  case HtmlOptionId::WIDTH:
943  {
944  mxActEntry->nWidth = GetWidthPixel(rOption);
945  }
946  break;
947  case HtmlOptionId::BGCOLOR:
948  {
949  Color aColor;
950  rOption.GetColor( aColor );
951  mxActEntry->aItemSet.Put(SvxBrushItem(aColor, ATTR_BACKGROUND));
952  }
953  break;
954  case HtmlOptionId::SDVAL:
955  {
956  mxActEntry->pValStr = rOption.GetString();
957  }
958  break;
959  case HtmlOptionId::SDNUM:
960  {
961  mxActEntry->pNumStr = rOption.GetString();
962  }
963  break;
964  default: break;
965  }
966  }
967 
968  mxActEntry->nCol = nColCnt;
969  mxActEntry->nRow = nRowCnt;
970  mxActEntry->nTab = nTable;
971 
972  if ( bHorJustifyCenterTH )
973  mxActEntry->aItemSet.Put(
974  SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY) );
975 }
976 
978 {
979  if ( nColCnt > nColCntStart )
980  NextRow( pInfo ); // The optional TableRowOff wasn't there
982 }
983 
985 {
986  NextRow( pInfo );
987 }
988 
990 {
991  if ( bInCell )
992  CloseEntry( pInfo ); // Only if it really was one
993 }
994 
996 {
997  if ( ++nTableLevel > 1 )
998  { // Table in Table
999  sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize()
1000  Colonize(mxActEntry.get());
1001  aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
1005  bFirstRow ) );
1006  sal_uInt16 nLastWidth = nTableWidth;
1007  nTableWidth = GetWidth(mxActEntry.get());
1008  if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
1009  { // There must be more than one, so this one cannot be enough
1010  nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
1011  }
1012  nLastWidth = nTableWidth;
1013  if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
1014  { // It can still be TD or TH, if we didn't have a TABLE earlier
1015  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1016  for (const auto & rOption : rOptions)
1017  {
1018  switch( rOption.GetToken() )
1019  {
1020  case HtmlOptionId::WIDTH:
1021  { // Percent: of document width or outer cell
1022  nTableWidth = GetWidthPixel( rOption );
1023  }
1024  break;
1025  case HtmlOptionId::BORDER:
1026  // Border is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1027  break;
1028  default: break;
1029  }
1030  }
1031  }
1032  bInCell = false;
1033  if ( bTabInTabCell && (nTableWidth >= nLastWidth) )
1034  { // Multiple tables in one cell, underneath each other
1035  bTabInTabCell = false;
1036  NextRow( pInfo );
1037  }
1038  else
1039  { // It start's in this cell or next to each other
1040  bTabInTabCell = false;
1042  nColOffset = nTmpColOffset;
1044  }
1045 
1046  NewActEntry(!maList.empty() ? maList.back().get() : nullptr); // New free flying mxActEntry
1047  xLockedList = new ScRangeList;
1048  }
1049  else
1050  { // Simple table at the document level
1051  EntryEnd(mxActEntry.get(), pInfo->aSelection);
1052  if (mxActEntry->aSel.HasRange())
1053  { // Flying text left
1054  CloseEntry( pInfo );
1055  NextRow( pInfo );
1056  }
1057  aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
1061  bFirstRow ) );
1062  // As soon as we have multiple tables we need to be tolerant with the offsets.
1063  if (nMaxTable > 0)
1065  nTableWidth = 0;
1066  if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
1067  {
1068  // It can still be TD or TH, if we didn't have a TABLE earlier
1069  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1070  for (const auto & rOption : rOptions)
1071  {
1072  switch( rOption.GetToken() )
1073  {
1074  case HtmlOptionId::WIDTH:
1075  { // Percent: of document width or outer cell
1076  nTableWidth = GetWidthPixel( rOption );
1077  }
1078  break;
1079  case HtmlOptionId::BORDER:
1080  //BorderOn is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1081  break;
1082  default: break;
1083  }
1084  }
1085  }
1086  }
1087  nTable = ++nMaxTable;
1088  bFirstRow = true;
1089  nFirstTableCell = maList.size();
1090 
1093 }
1094 
1096 {
1097  if ( bInCell )
1098  CloseEntry( pInfo );
1099  if ( nColCnt > nColCntStart )
1100  TableRowOff( pInfo ); // The optional TableRowOff wasn't
1101  if ( !nTableLevel )
1102  {
1103  OSL_FAIL( "dumbo doc! </TABLE> without opening <TABLE>" );
1104  return ;
1105  }
1106  if ( --nTableLevel > 0 )
1107  { // Table in Table done
1108  if ( !aTableStack.empty() )
1109  {
1110  std::unique_ptr<ScHTMLTableStackEntry> pS = std::move(aTableStack.top());
1111  aTableStack.pop();
1112 
1113  auto& pE = pS->xCellEntry;
1114  SCROW nRows = nRowCnt - pS->nRowCnt;
1115  if ( nRows > 1 )
1116  { // Insert size of table at this position
1117  SCROW nRow = pS->nRowCnt;
1118  sal_uInt16 nTab = pS->nTable;
1119  if ( !pTables )
1120  pTables.reset( new OuterMap );
1121  // Height of outer table
1122  OuterMap::const_iterator it = pTables->find( nTab );
1123  InnerMap* pTab1;
1124  if ( it == pTables->end() )
1125  {
1126  pTab1 = new InnerMap;
1127  (*pTables)[ nTab ] = pTab1;
1128  }
1129  else
1130  pTab1 = it->second;
1131  SCROW nRowSpan = pE->nRowOverlap;
1132  SCROW nRowKGV;
1133  SCROW nRowsPerRow1; // Outer table
1134  SCROW nRowsPerRow2; // Inner table
1135  if ( nRowSpan > 1 )
1136  { // LCM to which we can map the inner and outer rows
1137  nRowKGV = std::lcm( nRowSpan, nRows );
1138  nRowsPerRow1 = nRowKGV / nRowSpan;
1139  nRowsPerRow2 = nRowKGV / nRows;
1140  }
1141  else
1142  {
1143  nRowKGV = nRowsPerRow1 = nRows;
1144  nRowsPerRow2 = 1;
1145  }
1146  InnerMap* pTab2 = nullptr;
1147  if ( nRowsPerRow2 > 1 )
1148  { // Height of the inner table
1149  pTab2 = new InnerMap;
1150  (*pTables)[ nTable ] = pTab2;
1151  }
1152  // Abuse void* Data entry of the Table class for height mapping
1153  if ( nRowKGV > 1 )
1154  {
1155  if ( nRowsPerRow1 > 1 )
1156  { // Outer
1157  for ( SCROW j=0; j < nRowSpan; j++ )
1158  {
1159  sal_uLong nRowKey = nRow + j;
1160  SCROW nR = (*pTab1)[ nRowKey ];
1161  if ( !nR )
1162  (*pTab1)[ nRowKey ] = nRowsPerRow1;
1163  else if ( nRowsPerRow1 > nR )
1164  (*pTab1)[ nRowKey ] = nRowsPerRow1;
1165  //TODO: How can we improve on this?
1166  else if ( nRowsPerRow1 < nR && nRowSpan == 1
1167  && nTable == nMaxTable )
1168  { // Still some space left, merge in a better way (if possible)
1169  SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
1170  nR += nAdd;
1171  if ( (nR % nRows) == 0 )
1172  { // Only if representable
1173  SCROW nR2 = (*pTab1)[ nRowKey+1 ];
1174  if ( nR2 > nAdd )
1175  { // Only if we really have enough space
1176  (*pTab1)[ nRowKey ] = nR;
1177  (*pTab1)[ nRowKey+1 ] = nR2 - nAdd;
1178  nRowsPerRow2 = nR / nRows;
1179  }
1180  }
1181  }
1182  }
1183  }
1184  if ( nRowsPerRow2 > 1 )
1185  { // Inner
1186  if ( !pTab2 )
1187  { // nRowsPerRow2 could be've been incremented
1188  pTab2 = new InnerMap;
1189  (*pTables)[ nTable ] = pTab2;
1190  }
1191  for ( SCROW j=0; j < nRows; j++ )
1192  {
1193  sal_uLong nRowKey = nRow + j;
1194  (*pTab2)[ nRowKey ] = nRowsPerRow2;
1195  }
1196  }
1197  }
1198  }
1199 
1200  SetWidths();
1201 
1202  if ( !pE->nWidth )
1203  pE->nWidth = nTableWidth;
1204  else if ( pE->nWidth < nTableWidth )
1205  {
1206  sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
1207  sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
1208  ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
1209  sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
1210  pE->nWidth = nNewOffset - pE->nOffset;
1211  pS->nTableWidth = pS->nTableWidth + nTmp;
1212  if ( pS->nColOffset >= nOldOffset )
1213  pS->nColOffset = pS->nColOffset + nTmp;
1214  }
1215 
1216  nColCnt = pE->nCol + pE->nColOverlap;
1217  nRowCnt = pS->nRowCnt;
1218  nColCntStart = pS->nColCntStart;
1219  nMaxCol = pS->nMaxCol;
1220  nTable = pS->nTable;
1221  nTableWidth = pS->nTableWidth;
1222  nFirstTableCell = pS->nFirstTableCell;
1223  nColOffset = pS->nColOffset;
1224  nColOffsetStart = pS->nColOffsetStart;
1225  bFirstRow = pS->bFirstRow;
1226  xLockedList = pS->xLockedList;
1227  pLocalColOffset = pS->pLocalColOffset;
1228  // mxActEntry is kept around if a table is started in the same row
1229  // (anything's possible in HTML); will be deleted by CloseEntry
1230  mxActEntry = pE;
1231  }
1232  bTabInTabCell = true;
1233  bInCell = true;
1234  }
1235  else
1236  { // Simple table finished
1237  SetWidths();
1238  nMaxCol = 0;
1239  nTable = 0;
1240  if ( !aTableStack.empty() )
1241  {
1242  ScHTMLTableStackEntry* pS = aTableStack.top().get();
1243  delete pLocalColOffset;
1245  aTableStack.pop();
1246  }
1247  }
1248 }
1249 
1251 {
1252  mxActEntry->maImageList.push_back(std::make_unique<ScHTMLImage>());
1253  ScHTMLImage* pImage = mxActEntry->maImageList.back().get();
1254  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1255  for (const auto & rOption : rOptions)
1256  {
1257  switch( rOption.GetToken() )
1258  {
1259  case HtmlOptionId::SRC:
1260  {
1261  pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, rOption.GetString() );
1262  }
1263  break;
1264  case HtmlOptionId::ALT:
1265  {
1266  if (!mxActEntry->bHasGraphic)
1267  { // ALT text only if not any image loaded
1268  if (!mxActEntry->aAltText.isEmpty())
1269  mxActEntry->aAltText += "; ";
1270 
1271  mxActEntry->aAltText += rOption.GetString();
1272  }
1273  }
1274  break;
1275  case HtmlOptionId::WIDTH:
1276  {
1277  pImage->aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
1278  }
1279  break;
1280  case HtmlOptionId::HEIGHT:
1281  {
1282  pImage->aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
1283  }
1284  break;
1285  case HtmlOptionId::HSPACE:
1286  {
1287  pImage->aSpace.setX( static_cast<tools::Long>(rOption.GetNumber()) );
1288  }
1289  break;
1290  case HtmlOptionId::VSPACE:
1291  {
1292  pImage->aSpace.setY( static_cast<tools::Long>(rOption.GetNumber()) );
1293  }
1294  break;
1295  default: break;
1296  }
1297  }
1298  if (pImage->aURL.isEmpty())
1299  {
1300  OSL_FAIL( "Image: graphic without URL ?!?" );
1301  return ;
1302  }
1303 
1304  sal_uInt16 nFormat;
1305  std::unique_ptr<Graphic> pGraphic(new Graphic);
1307  if ( ERRCODE_NONE != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
1308  *pGraphic, &rFilter, &nFormat ) )
1309  {
1310  return ; // Bad luck
1311  }
1312  if (!mxActEntry->bHasGraphic)
1313  { // discard any ALT text in this cell if we have any image
1314  mxActEntry->bHasGraphic = true;
1315  mxActEntry->aAltText.clear();
1316  }
1317  pImage->aFilterName = rFilter.GetImportFormatName( nFormat );
1318  pImage->pGraphic = std::move( pGraphic );
1319  if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
1320  {
1322  pImage->aSize = pDefaultDev->LogicToPixel( pImage->pGraphic->GetPrefSize(),
1323  pImage->pGraphic->GetPrefMapMode() );
1324  }
1325  if (mxActEntry->maImageList.empty())
1326  return;
1327 
1328  tools::Long nWidth = 0;
1329  for (const std::unique_ptr<ScHTMLImage> & pI : mxActEntry->maImageList)
1330  {
1331  if ( pI->nDir & nHorizontal )
1332  nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
1333  else
1334  nWidth = 0;
1335  }
1336  if ( mxActEntry->nWidth
1337  && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
1338  >= mxActEntry->nWidth) )
1339  mxActEntry->maImageList.back()->nDir = nVertical;
1340 }
1341 
1343 {
1344  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1345  for (const auto & rOption : rOptions)
1346  {
1347  if( rOption.GetToken() == HtmlOptionId::WIDTH )
1348  {
1349  sal_uInt16 nVal = GetWidthPixel( rOption );
1350  MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
1351  nColOffset = nColOffset + nVal;
1352  }
1353  }
1354 }
1355 
1356 sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption& rOption )
1357 {
1358  const OUString& rOptVal = rOption.GetString();
1359  if ( rOptVal.indexOf('%') != -1 )
1360  { // Percent
1361  sal_uInt16 nW = (nTableWidth ? nTableWidth : static_cast<sal_uInt16>(aPageSize.Width()));
1362  return static_cast<sal_uInt16>((rOption.GetNumber() * nW) / 100);
1363  }
1364  else
1365  {
1366  if ( rOptVal.indexOf('*') != -1 )
1367  { // Relative to what?
1368  // TODO: Collect all relative values in ColArray and then MakeCol
1369  return 0;
1370  }
1371  else
1372  return static_cast<sal_uInt16>(rOption.GetNumber()); // Pixel
1373  }
1374 }
1375 
1377 {
1378  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1379  for (const auto & rOption : rOptions)
1380  {
1381  if( rOption.GetToken() == HtmlOptionId::NAME )
1382  mxActEntry->pName = rOption.GetString();
1383  }
1384 }
1385 
1387 {
1388  ESelection& rSel = mxActEntry->aSel;
1389  return rSel.nStartPara == rSel.nEndPara &&
1390  rSel.nStartPara <= pInfo->aSelection.nEndPara &&
1391  pEdit->GetTextLen( rSel.nStartPara ) == 0;
1392 }
1393 
1395 {
1396  if ( !IsAtBeginningOfText( pInfo ) )
1397  return;
1398 
1399 // Only at the start of the text; applies to whole line
1400  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1401  for (const auto & rOption : rOptions)
1402  {
1403  switch( rOption.GetToken() )
1404  {
1405  case HtmlOptionId::FACE :
1406  {
1407  const OUString& rFace = rOption.GetString();
1408  OUStringBuffer aFontName;
1409  sal_Int32 nPos = 0;
1410  while( nPos != -1 )
1411  {
1412  // Font list, VCL uses the semicolon as separator
1413  // HTML uses the comma
1414  OUString aFName = rFace.getToken( 0, ',', nPos );
1415  aFName = comphelper::string::strip(aFName, ' ');
1416  if( !aFontName.isEmpty() )
1417  aFontName.append(";");
1418  aFontName.append(aFName);
1419  }
1420  if ( !aFontName.isEmpty() )
1421  mxActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
1422  aFontName.makeStringAndClear(), EMPTY_OUSTRING, PITCH_DONTKNOW,
1423  RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
1424  }
1425  break;
1426  case HtmlOptionId::SIZE :
1427  {
1428  sal_uInt16 nSize = static_cast<sal_uInt16>(rOption.GetNumber());
1429  if ( nSize == 0 )
1430  nSize = 1;
1431  else if ( nSize > SC_HTML_FONTSIZES )
1432  nSize = SC_HTML_FONTSIZES;
1433  mxActEntry->aItemSet.Put( SvxFontHeightItem(
1434  maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
1435  }
1436  break;
1437  case HtmlOptionId::COLOR :
1438  {
1439  Color aColor;
1440  rOption.GetColor( aColor );
1441  mxActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
1442  }
1443  break;
1444  default: break;
1445  }
1446  }
1447 }
1448 
1450 {
1451  switch ( pInfo->nToken )
1452  {
1453  case HtmlTokenId::META:
1454  {
1455  HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser);
1456  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1457  mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
1458  pParser->ParseMetaOptions(
1459  xDPS->getDocumentProperties(),
1461  }
1462  break;
1463  case HtmlTokenId::TITLE_ON:
1464  {
1465  bInTitle = true;
1466  aString.clear();
1467  }
1468  break;
1469  case HtmlTokenId::TITLE_OFF:
1470  {
1471  if ( bInTitle && !aString.isEmpty() )
1472  {
1473  // Remove blanks from line brakes
1474  aString = aString.trim();
1475  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1477  uno::UNO_QUERY_THROW);
1478  xDPS->getDocumentProperties()->setTitle(aString);
1479  }
1480  bInTitle = false;
1481  }
1482  break;
1483  case HtmlTokenId::TABLE_ON:
1484  {
1485  TableOn( pInfo );
1486  }
1487  break;
1488  case HtmlTokenId::COL_ON:
1489  {
1490  ColOn( pInfo );
1491  }
1492  break;
1493  case HtmlTokenId::TABLEHEADER_ON: // Opens row
1494  {
1495  if ( bInCell )
1496  CloseEntry( pInfo );
1497  // Do not set bInCell to true, TableDataOn does that
1498  mxActEntry->aItemSet.Put(
1500  [[fallthrough]];
1501  }
1502  case HtmlTokenId::TABLEDATA_ON: // Opens cell
1503  {
1504  TableDataOn( pInfo );
1505  }
1506  break;
1507  case HtmlTokenId::TABLEHEADER_OFF:
1508  case HtmlTokenId::TABLEDATA_OFF: // Closes cell
1509  {
1510  TableDataOff( pInfo );
1511  }
1512  break;
1513  case HtmlTokenId::TABLEROW_ON: // Before first cell in row
1514  {
1515  TableRowOn( pInfo );
1516  }
1517  break;
1518  case HtmlTokenId::TABLEROW_OFF: // After last cell in row
1519  {
1520  TableRowOff( pInfo );
1521  }
1522  break;
1523  case HtmlTokenId::TABLE_OFF:
1524  {
1525  TableOff( pInfo );
1526  }
1527  break;
1528  case HtmlTokenId::IMAGE:
1529  {
1530  Image( pInfo );
1531  }
1532  break;
1533  case HtmlTokenId::PARABREAK_OFF:
1534  { // We continue vertically after an image
1535  if (!mxActEntry->maImageList.empty())
1536  mxActEntry->maImageList.back()->nDir = nVertical;
1537  }
1538  break;
1539  case HtmlTokenId::ANCHOR_ON:
1540  {
1541  AnchorOn( pInfo );
1542  }
1543  break;
1544  case HtmlTokenId::FONT_ON :
1545  {
1546  FontOn( pInfo );
1547  }
1548  break;
1549  case HtmlTokenId::BIGPRINT_ON :
1550  {
1551  // TODO: Remember current font size and increase by 1
1552  if ( IsAtBeginningOfText( pInfo ) )
1553  mxActEntry->aItemSet.Put( SvxFontHeightItem(
1554  maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
1555  }
1556  break;
1557  case HtmlTokenId::SMALLPRINT_ON :
1558  {
1559  // TODO: Remember current font size and decrease by 1
1560  if ( IsAtBeginningOfText( pInfo ) )
1561  mxActEntry->aItemSet.Put( SvxFontHeightItem(
1562  maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
1563  }
1564  break;
1565  case HtmlTokenId::BOLD_ON :
1566  case HtmlTokenId::STRONG_ON :
1567  {
1568  if ( IsAtBeginningOfText( pInfo ) )
1569  mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1570  ATTR_FONT_WEIGHT ) );
1571  }
1572  break;
1573  case HtmlTokenId::ITALIC_ON :
1574  case HtmlTokenId::EMPHASIS_ON :
1575  case HtmlTokenId::ADDRESS_ON :
1576  case HtmlTokenId::BLOCKQUOTE_ON :
1577  case HtmlTokenId::BLOCKQUOTE30_ON :
1578  case HtmlTokenId::CITATION_ON :
1579  case HtmlTokenId::VARIABLE_ON :
1580  {
1581  if ( IsAtBeginningOfText( pInfo ) )
1582  mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1583  ATTR_FONT_POSTURE ) );
1584  }
1585  break;
1586  case HtmlTokenId::DEFINSTANCE_ON :
1587  {
1588  if ( IsAtBeginningOfText( pInfo ) )
1589  {
1590  mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1591  ATTR_FONT_WEIGHT ) );
1592  mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1593  ATTR_FONT_POSTURE ) );
1594  }
1595  }
1596  break;
1597  case HtmlTokenId::UNDERLINE_ON :
1598  {
1599  if ( IsAtBeginningOfText( pInfo ) )
1601  ATTR_FONT_UNDERLINE ) );
1602  }
1603  break;
1604  case HtmlTokenId::TEXTTOKEN:
1605  {
1606  if ( bInTitle )
1607  aString += pInfo->aText;
1608  }
1609  break;
1610  default: ;
1611  }
1612 }
1613 
1614 // HTML DATA QUERY PARSER
1615 
1616 template< typename Type >
1617 static Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
1618 { return std::clamp( rValue, rMin, rMax ); }
1619 
1620 ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
1621  ScEEParseEntry( rItemSet ),
1622  mbImportAlways( false )
1623 {
1624  nTab = nTableId;
1625  bEntirePara = false;
1626 }
1627 
1629 {
1630  return mbImportAlways || aSel.HasRange() || !aAltText.isEmpty() || IsTable();
1631 }
1632 
1634 {
1635  // set start position
1638  // adjust end position
1640  {
1643  }
1644 }
1645 
1647 {
1648  OSL_ENSURE( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
1649  ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
1650  "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
1651  // set end position
1652  aSel.nEndPara = rInfo.aSelection.nEndPara;
1653  aSel.nEndPos = rInfo.aSelection.nEndPos;
1654 }
1655 
1656 void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
1657 {
1658  // strip leading empty paragraphs
1659  while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
1660  {
1661  ++aSel.nStartPara;
1662  aSel.nStartPos = 0;
1663  }
1664  // strip trailing empty paragraphs
1665  while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
1666  {
1667  --aSel.nEndPara;
1668  aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
1669  }
1670 }
1671 
1679 class ScHTMLTableMap final
1680 {
1681 private:
1682  typedef std::shared_ptr< ScHTMLTable > ScHTMLTablePtr;
1683  typedef std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
1684 
1685 public:
1686  typedef ScHTMLTableStdMap::iterator iterator;
1687  typedef ScHTMLTableStdMap::const_iterator const_iterator;
1688 
1689 private:
1691  ScHTMLTableStdMap maTables;
1693 
1694 public:
1695  explicit ScHTMLTableMap( ScHTMLTable& rParentTable );
1696 
1697  const_iterator begin() const { return maTables.begin(); }
1698  const_iterator end() const { return maTables.end(); }
1699 
1703  ScHTMLTable* FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
1704 
1707  ScHTMLTable* CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText );
1708 
1709 private:
1711  void SetCurrTable( ScHTMLTable* pTable ) const
1712  { if( pTable ) mpCurrTable = pTable; }
1713 };
1714 
1716  mrParentTable(rParentTable),
1717  mpCurrTable(nullptr)
1718 {
1719 }
1720 
1722 {
1723  ScHTMLTable* pResult = nullptr;
1724  if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
1725  pResult = mpCurrTable; // cached table
1726  else
1727  {
1728  const_iterator aFind = maTables.find( nTableId );
1729  if( aFind != maTables.end() )
1730  pResult = aFind->second.get(); // table from this container
1731  }
1732 
1733  // not found -> search deep in nested tables
1734  if( !pResult && bDeep )
1735  for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
1736  pResult = aIter->second->FindNestedTable( nTableId );
1737 
1738  SetCurrTable( pResult );
1739  return pResult;
1740 }
1741 
1742 ScHTMLTable* ScHTMLTableMap::CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText )
1743 {
1744  ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText );
1745  maTables[ pTable->GetTableId() ].reset( pTable );
1746  SetCurrTable( pTable );
1747  return pTable;
1748 }
1749 
1750 namespace {
1751 
1758 class ScHTMLTableIterator
1759 {
1760 public:
1763  explicit ScHTMLTableIterator( const ScHTMLTableMap* pTableMap );
1764 
1765  bool is() const { return mpTableMap && maIter != maEnd; }
1766  ScHTMLTable* operator->() { return maIter->second.get(); }
1767  ScHTMLTableIterator& operator++() { ++maIter; return *this; }
1768 
1769 private:
1772  const ScHTMLTableMap* mpTableMap;
1773 };
1774 
1775 }
1776 
1777 ScHTMLTableIterator::ScHTMLTableIterator( const ScHTMLTableMap* pTableMap ) :
1778  mpTableMap(pTableMap)
1779 {
1780  if( pTableMap )
1781  {
1782  maIter = pTableMap->begin();
1783  maEnd = pTableMap->end();
1784  }
1785 }
1786 
1788  mnTableId( rnUnusedId ),
1789  mrnUnusedId( rnUnusedId )
1790 {
1791  ++mrnUnusedId;
1792 }
1793 
1794 ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const HtmlImportInfo& rInfo, bool bPreFormText ) :
1795  mpParentTable( &rParentTable ),
1796  maTableId( rParentTable.maTableId.mrnUnusedId ),
1797  maTableItemSet( rParentTable.GetCurrItemSet() ),
1798  mrEditEngine( rParentTable.mrEditEngine ),
1799  mrEEParseList( rParentTable.mrEEParseList ),
1800  mpCurrEntryVector( nullptr ),
1801  maSize( 1, 1 ),
1802  mpParser(rParentTable.mpParser),
1803  mbBorderOn( false ),
1804  mbPreFormText( bPreFormText ),
1805  mbRowOn( false ),
1806  mbDataOn( false ),
1807  mbPushEmptyLine( false )
1808 {
1809  if( mbPreFormText )
1810  {
1811  ImplRowOn();
1812  ImplDataOn( ScHTMLSize( 1, 1 ) );
1813  }
1814  else
1815  {
1817  const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
1818  for (const auto& rOption : rOptions)
1819  {
1820  switch( rOption.GetToken() )
1821  {
1822  case HtmlOptionId::BORDER:
1823  mbBorderOn = rOption.GetString().isEmpty() || (rOption.GetNumber() != 0);
1824  break;
1825  case HtmlOptionId::ID:
1826  maTableName = rOption.GetString();
1827  break;
1828  default: break;
1829  }
1830  }
1831  }
1832 
1833  CreateNewEntry( rInfo );
1834 }
1835 
1837  SfxItemPool& rPool,
1838  EditEngine& rEditEngine,
1839  std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
1840  ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser
1841 ) :
1842  mpParentTable( nullptr ),
1843  maTableId( rnUnusedId ),
1844  maTableItemSet( rPool ),
1845  mrEditEngine( rEditEngine ),
1846  mrEEParseList( rEEParseList ),
1847  mpCurrEntryVector( nullptr ),
1848  maSize( 1, 1 ),
1849  mpParser(pParser),
1850  mbBorderOn( false ),
1851  mbPreFormText( false ),
1852  mbRowOn( false ),
1853  mbDataOn( false ),
1854  mbPushEmptyLine( false )
1855 {
1856  // open the first "cell" of the document
1857  ImplRowOn();
1858  ImplDataOn( ScHTMLSize( 1, 1 ) );
1860 }
1861 
1863 {
1864 }
1865 
1867 {
1868  // first try cell item set, then row item set, then table item set
1870 }
1871 
1873 {
1874  ScHTMLSize aSpan( 1, 1 );
1875  const ScRange* pRange = maVMergedCells.Find( rCellPos.MakeAddr() );
1876  if (!pRange)
1877  pRange = maHMergedCells.Find( rCellPos.MakeAddr() );
1878  if (pRange)
1879  aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
1880  return aSpan;
1881 }
1882 
1884 {
1885  return mxNestedTables ? mxNestedTables->FindTable( nTableId ) : nullptr;
1886 }
1887 
1888 void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
1889 {
1890  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutItem - no current entry" );
1891  if( mxCurrEntry && mxCurrEntry->IsEmpty() )
1892  mxCurrEntry->GetItemSet().Put( rItem );
1893 }
1894 
1896 {
1897  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutText - no current entry" );
1898  if( mxCurrEntry )
1899  {
1900  if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
1901  mxCurrEntry->AdjustStart( rInfo );
1902  else
1903  mxCurrEntry->AdjustEnd( rInfo );
1904  }
1905 }
1906 
1908 {
1909  if( mxCurrEntry && mbDataOn && !IsEmptyCell() )
1910  mxCurrEntry->SetImportAlways();
1911  PushEntry( rInfo );
1912  CreateNewEntry( rInfo );
1914 }
1915 
1917 {
1918  // empty line, if <br> is at start of cell
1920 }
1921 
1923 {
1924  // call directly, InsertPara() has not been called before
1926 }
1927 
1929 {
1930  // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
1932 }
1933 
1935 {
1936  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::AnchorOn - no current entry" );
1937  // don't skip entries with single hyperlinks
1938  if( mxCurrEntry )
1939  mxCurrEntry->SetImportAlways();
1940 }
1941 
1943 {
1944  PushEntry( rInfo );
1945  return InsertNestedTable( rInfo, false );
1946 }
1947 
1949 {
1950  return mbPreFormText ? this : CloseTable( rInfo );
1951 }
1952 
1954 {
1955  PushEntry( rInfo );
1956  return InsertNestedTable( rInfo, true );
1957 }
1958 
1960 {
1961  return mbPreFormText ? CloseTable( rInfo ) : this;
1962 }
1963 
1965 {
1966  PushEntry( rInfo, true );
1967  if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
1968  {
1969  ImplRowOn();
1971  }
1972  CreateNewEntry( rInfo );
1973 }
1974 
1976 {
1977  PushEntry( rInfo, true );
1978  if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
1979  ImplRowOff();
1980  CreateNewEntry( rInfo );
1981 }
1982 
1983 namespace {
1984 
1989 OUString decodeNumberFormat(const OUString& rFmt)
1990 {
1991  OUStringBuffer aBuf;
1992  const sal_Unicode* p = rFmt.getStr();
1993  sal_Int32 n = rFmt.getLength();
1994  for (sal_Int32 i = 0; i < n; ++i, ++p)
1995  {
1996  if (*p == '\\')
1997  {
1998  // Skip '\'.
1999  ++i;
2000  ++p;
2001 
2002  // Parse all subsequent digits until first non-digit is found.
2003  sal_Int32 nDigitCount = 0;
2004  const sal_Unicode* p1 = p;
2005  for (; i < n; ++i, ++p, ++nDigitCount)
2006  {
2007  if (*p < '0' || '9' < *p)
2008  {
2009  --i;
2010  --p;
2011  break;
2012  }
2013 
2014  }
2015  if (nDigitCount)
2016  {
2017  // Hex-encoded character found. Decode it back into its
2018  // original character. An example of number format with
2019  // hex-encoded chars: "\0022$\0022\#\,\#\#0\.00"
2020  sal_uInt32 nVal = OUString(p1, nDigitCount).toUInt32(16);
2021  aBuf.append(static_cast<sal_Unicode>(nVal));
2022  }
2023  }
2024  else
2025  aBuf.append(*p);
2026  }
2027  return aBuf.makeStringAndClear();
2028 }
2029 
2030 }
2031 
2033 {
2034  PushEntry( rInfo, true );
2035  if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
2036  {
2037  // read needed options from the <td> tag
2038  ScHTMLSize aSpanSize( 1, 1 );
2039  std::optional<OUString> pValStr, pNumStr;
2040  const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2041  sal_uInt32 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
2042  for (const auto& rOption : rOptions)
2043  {
2044  switch (rOption.GetToken())
2045  {
2046  case HtmlOptionId::COLSPAN:
2047  aSpanSize.mnCols = static_cast<SCCOL>( getLimitedValue<sal_Int32>( rOption.GetString().toInt32(), 1, 256 ) );
2048  break;
2049  case HtmlOptionId::ROWSPAN:
2050  aSpanSize.mnRows = static_cast<SCROW>( getLimitedValue<sal_Int32>( rOption.GetString().toInt32(), 1, 256 ) );
2051  break;
2052  case HtmlOptionId::SDVAL:
2053  pValStr = rOption.GetString();
2054  break;
2055  case HtmlOptionId::SDNUM:
2056  pNumStr = rOption.GetString();
2057  break;
2058  case HtmlOptionId::CLASS:
2059  {
2060  // Pick up the number format associated with this class (if
2061  // any).
2062  OUString aClass = rOption.GetString();
2063  const ScHTMLStyles& rStyles = mpParser->GetStyles();
2064  const OUString& rVal = rStyles.getPropertyValue("td", aClass, "mso-number-format");
2065  if (!rVal.isEmpty())
2066  {
2067  OUString aNumFmt = decodeNumberFormat(rVal);
2068 
2069  nNumberFormat = GetFormatTable()->GetEntryKey(aNumFmt);
2070  if (nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
2071  {
2072  sal_Int32 nErrPos = 0;
2073  SvNumFormatType nDummy;
2074  bool bValidFmt = GetFormatTable()->PutEntry(aNumFmt, nErrPos, nDummy, nNumberFormat);
2075  if (!bValidFmt)
2076  nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
2077  }
2078  }
2079  }
2080  break;
2081  default: break;
2082  }
2083  }
2084 
2085  ImplDataOn( aSpanSize );
2086 
2087  if (nNumberFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
2088  mxDataItemSet->Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat) );
2089 
2091  CreateNewEntry( rInfo );
2092  mxCurrEntry->pValStr = std::move(pValStr);
2093  mxCurrEntry->pNumStr = std::move(pNumStr);
2094  }
2095  else
2096  CreateNewEntry( rInfo );
2097 }
2098 
2100 {
2101  PushEntry( rInfo, true );
2102  if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
2103  ImplDataOff();
2104  CreateNewEntry( rInfo );
2105 }
2106 
2108 {
2109  bool bPushed = PushEntry( rInfo );
2110  if( !mpParentTable )
2111  {
2112  // do not start new row, if nothing (no title) precedes the body.
2113  if( bPushed || !mbRowOn )
2114  ImplRowOn();
2115  if( bPushed || !mbDataOn )
2116  ImplDataOn( ScHTMLSize( 1, 1 ) );
2118  }
2119  CreateNewEntry( rInfo );
2120 }
2121 
2123 {
2124  PushEntry( rInfo );
2125  if( !mpParentTable )
2126  {
2127  ImplDataOff();
2128  ImplRowOff();
2129  }
2130  CreateNewEntry( rInfo );
2131 }
2132 
2134 {
2135  if( mpParentTable ) // not allowed to close global table
2136  {
2137  PushEntry( rInfo, mbDataOn );
2138  ImplDataOff();
2139  ImplRowOff();
2141  mpParentTable->CreateNewEntry( rInfo );
2142  if( mbPreFormText ) // enclose preformatted table with empty lines in parent table
2144  return mpParentTable;
2145  }
2146  return this;
2147 }
2148 
2150 {
2151  const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2152  size_t nIndex = static_cast< size_t >( nCellPos );
2153  if( nIndex >= rSizes.size() ) return 0;
2154  return (nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]);
2155 }
2156 
2157 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const
2158 {
2159  const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2160  size_t nBeginIdx = static_cast< size_t >( std::max< SCCOLROW >( nCellBegin, 0 ) );
2161  size_t nEndIdx = static_cast< size_t >( std::min< SCCOLROW >( nCellEnd, static_cast< SCCOLROW >( rSizes.size() ) ) );
2162  if (nBeginIdx >= nEndIdx ) return 0;
2163  return rSizes[ nEndIdx - 1 ] - ((nBeginIdx == 0) ? 0 : rSizes[ nBeginIdx - 1 ]);
2164 }
2165 
2167 {
2168  const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2169  return rSizes.empty() ? 0 : rSizes.back();
2170 }
2171 
2173 {
2174  ScHTMLSize aCellSpan = GetSpan( rCellPos );
2175  return ScHTMLSize(
2176  static_cast< SCCOL >( GetDocSize( tdCol, rCellPos.mnCol, rCellPos.mnCol + aCellSpan.mnCols ) ),
2177  static_cast< SCROW >( GetDocSize( tdRow, rCellPos.mnRow, rCellPos.mnRow + aCellSpan.mnRows ) ) );
2178 }
2179 
2181 {
2182  return maDocBasePos.Get( eOrient ) + GetDocSize( eOrient, 0, nCellPos );
2183 }
2184 
2186 {
2187  return ScHTMLPos(
2188  static_cast< SCCOL >( GetDocPos( tdCol, rCellPos.mnCol ) ),
2189  static_cast< SCROW >( GetDocPos( tdRow, rCellPos.mnRow ) ) );
2190 }
2191 
2192 void ScHTMLTable::GetDocRange( ScRange& rRange ) const
2193 {
2194  rRange.aStart = rRange.aEnd = maDocBasePos.MakeAddr();
2195  ScAddress aErrorPos( ScAddress::UNINITIALIZED );
2196  if (!rRange.aEnd.Move( static_cast< SCCOL >( GetDocSize( tdCol ) ) - 1,
2197  static_cast< SCROW >( GetDocSize( tdRow ) ) - 1, 0, aErrorPos))
2198  {
2199  assert(!"can't move");
2200  }
2201 }
2202 
2203 void ScHTMLTable::ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const
2204 {
2205  OSL_ENSURE( pDoc, "ScHTMLTable::ApplyCellBorders - no document" );
2206  if( pDoc && mbBorderOn )
2207  {
2208  const SCCOL nLastCol = maSize.mnCols - 1;
2209  const SCROW nLastRow = maSize.mnRows - 1;
2210  const tools::Long nOuterLine = DEF_LINE_WIDTH_2;
2211  const tools::Long nInnerLine = DEF_LINE_WIDTH_0;
2212  SvxBorderLine aOuterLine(nullptr, nOuterLine, SvxBorderLineStyle::SOLID);
2213  SvxBorderLine aInnerLine(nullptr, nInnerLine, SvxBorderLineStyle::SOLID);
2214  SvxBoxItem aBorderItem( ATTR_BORDER );
2215 
2216  for( SCCOL nCol = 0; nCol <= nLastCol; ++nCol )
2217  {
2218  SvxBorderLine* pLeftLine = (nCol == 0) ? &aOuterLine : &aInnerLine;
2219  SvxBorderLine* pRightLine = (nCol == nLastCol) ? &aOuterLine : &aInnerLine;
2220  SCCOL nCellCol1 = static_cast< SCCOL >( GetDocPos( tdCol, nCol ) ) + rFirstPos.Col();
2221  SCCOL nCellCol2 = nCellCol1 + static_cast< SCCOL >( GetDocSize( tdCol, nCol ) ) - 1;
2222  for( SCROW nRow = 0; nRow <= nLastRow; ++nRow )
2223  {
2224  SvxBorderLine* pTopLine = (nRow == 0) ? &aOuterLine : &aInnerLine;
2225  SvxBorderLine* pBottomLine = (nRow == nLastRow) ? &aOuterLine : &aInnerLine;
2226  SCROW nCellRow1 = GetDocPos( tdRow, nRow ) + rFirstPos.Row();
2227  SCROW nCellRow2 = nCellRow1 + GetDocSize( tdRow, nRow ) - 1;
2228  for( SCCOL nCellCol = nCellCol1; nCellCol <= nCellCol2; ++nCellCol )
2229  {
2230  aBorderItem.SetLine( (nCellCol == nCellCol1) ? pLeftLine : nullptr, SvxBoxItemLine::LEFT );
2231  aBorderItem.SetLine( (nCellCol == nCellCol2) ? pRightLine : nullptr, SvxBoxItemLine::RIGHT );
2232  for( SCROW nCellRow = nCellRow1; nCellRow <= nCellRow2; ++nCellRow )
2233  {
2234  aBorderItem.SetLine( (nCellRow == nCellRow1) ? pTopLine : nullptr, SvxBoxItemLine::TOP );
2235  aBorderItem.SetLine( (nCellRow == nCellRow2) ? pBottomLine : nullptr, SvxBoxItemLine::BOTTOM );
2236  pDoc->ApplyAttr( nCellCol, nCellRow, rFirstPos.Tab(), aBorderItem );
2237  }
2238  }
2239  }
2240  }
2241  }
2242 
2243  for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2244  aIter->ApplyCellBorders( pDoc, rFirstPos );
2245 }
2246 
2248 {
2249  return mpParser->GetDoc().GetFormatTable();
2250 }
2251 
2253 {
2254  return mpCurrEntryVector && mpCurrEntryVector->empty();
2255 }
2256 
2258 {
2259  return (rInfo.nToken == HtmlTokenId::TEXTTOKEN) && (rInfo.aText.getLength() == 1) && (rInfo.aText[ 0 ] == ' ');
2260 }
2261 
2263 {
2264  return std::make_unique<ScHTMLEntry>( GetCurrItemSet() );
2265 }
2266 
2268 {
2269  OSL_ENSURE( !mxCurrEntry, "ScHTMLTable::CreateNewEntry - old entry still present" );
2271  mxCurrEntry->aSel = rInfo.aSelection;
2272 }
2273 
2275 {
2276  // HTML entry list does not own the entries
2277  rEntryVector.push_back( rxEntry.get() );
2278  // mrEEParseList (reference to member of ScEEParser) owns the entries
2279  mrEEParseList.push_back(std::shared_ptr<ScEEParseEntry>(rxEntry.release()));
2280 }
2281 
2283 {
2284  bool bPushed = false;
2285  if( rxEntry && rxEntry->HasContents() )
2286  {
2287  if( mpCurrEntryVector )
2288  {
2289  if( mbPushEmptyLine )
2290  {
2291  ScHTMLEntryPtr xEmptyEntry = CreateEntry();
2292  ImplPushEntryToVector( *mpCurrEntryVector, xEmptyEntry );
2293  mbPushEmptyLine = false;
2294  }
2296  bPushed = true;
2297  }
2298  else if( mpParentTable )
2299  {
2300  bPushed = mpParentTable->PushEntry( rxEntry );
2301  }
2302  else
2303  {
2304  OSL_FAIL( "ScHTMLTable::PushEntry - cannot push entry, no parent found" );
2305  }
2306  }
2307  return bPushed;
2308 }
2309 
2310 bool ScHTMLTable::PushEntry( const HtmlImportInfo& rInfo, bool bLastInCell )
2311 {
2312  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PushEntry - no current entry" );
2313  bool bPushed = false;
2314  if( mxCurrEntry )
2315  {
2316  mxCurrEntry->AdjustEnd( rInfo );
2317  mxCurrEntry->Strip( mrEditEngine );
2318 
2319  // import entry always, if it is the last in cell, and cell is still empty
2320  if( bLastInCell && IsEmptyCell() )
2321  {
2322  mxCurrEntry->SetImportAlways();
2323  // don't insert empty lines before single empty entries
2324  if( mxCurrEntry->IsEmpty() )
2325  mbPushEmptyLine = false;
2326  }
2327 
2328  bPushed = PushEntry( mxCurrEntry );
2329  mxCurrEntry.reset();
2330  }
2331  return bPushed;
2332 }
2333 
2335 {
2336  OSL_ENSURE( nTableId != SC_HTML_GLOBAL_TABLE, "ScHTMLTable::PushTableEntry - cannot push global table" );
2337  if( nTableId != SC_HTML_GLOBAL_TABLE )
2338  {
2339  ScHTMLEntryPtr xEntry( new ScHTMLEntry( maTableItemSet, nTableId ) );
2340  PushEntry( xEntry );
2341  }
2342 }
2343 
2345 {
2346  ScHTMLTable* pTable = ((nTableId != SC_HTML_GLOBAL_TABLE) && mxNestedTables) ?
2347  mxNestedTables->FindTable( nTableId, false ) : nullptr;
2348  OSL_ENSURE( pTable || (nTableId == SC_HTML_GLOBAL_TABLE), "ScHTMLTable::GetExistingTable - table not found" );
2349  return pTable;
2350 }
2351 
2352 ScHTMLTable* ScHTMLTable::InsertNestedTable( const HtmlImportInfo& rInfo, bool bPreFormText )
2353 {
2354  if( !mxNestedTables )
2355  mxNestedTables.reset( new ScHTMLTableMap( *this ) );
2356  if( bPreFormText ) // enclose new preformatted table with empty lines
2358  return mxNestedTables->CreateTable( rInfo, bPreFormText );
2359 }
2360 
2361 void ScHTMLTable::InsertNewCell( const ScHTMLSize& rSpanSize )
2362 {
2363  ScRange* pRange;
2364 
2365  /* Find an unused cell by skipping all merged ranges that cover the
2366  current cell position stored in maCurrCell. */
2367  for (;;)
2368  {
2369  pRange = maVMergedCells.Find( maCurrCell.MakeAddr() );
2370  if (!pRange)
2371  pRange = maHMergedCells.Find( maCurrCell.MakeAddr() );
2372  if (!pRange)
2373  break;
2374  maCurrCell.mnCol = pRange->aEnd.Col() + 1;
2375  }
2377 
2378  /* If the new cell is merged horizontally, try to find collisions with
2379  other vertically merged ranges. In this case, shrink existing
2380  vertically merged ranges (do not shrink the new cell). */
2381  SCCOL nColEnd = maCurrCell.mnCol + rSpanSize.mnCols;
2382  for( ScAddress aAddr( maCurrCell.MakeAddr() ); aAddr.Col() < nColEnd; aAddr.IncCol() )
2383  if( (pRange = maVMergedCells.Find( aAddr )) != nullptr )
2384  pRange->aEnd.SetRow( maCurrCell.mnRow - 1 );
2385 
2386  // insert the new range into the cell lists
2387  ScRange aNewRange( maCurrCell.MakeAddr() );
2388  ScAddress aErrorPos( ScAddress::UNINITIALIZED );
2389  if (!aNewRange.aEnd.Move( rSpanSize.mnCols - 1, rSpanSize.mnRows - 1, 0, aErrorPos))
2390  {
2391  assert(!"can't move");
2392  }
2393  if( rSpanSize.mnRows > 1 )
2394  {
2395  maVMergedCells.push_back( aNewRange );
2396  /* Do not insert vertically merged ranges into maUsedCells yet,
2397  because they may be shrunken (see above). The final vertically
2398  merged ranges are inserted in FillEmptyCells(). */
2399  }
2400  else
2401  {
2402  if( rSpanSize.mnCols > 1 )
2403  maHMergedCells.push_back( aNewRange );
2404  /* Insert horizontally merged ranges and single cells into
2405  maUsedCells, they will not be changed anymore. */
2406  maUsedCells.Join( aNewRange );
2407  }
2408 
2409  // adjust table size
2410  maSize.mnCols = std::max< SCCOL >( maSize.mnCols, aNewRange.aEnd.Col() + 1 );
2411  maSize.mnRows = std::max< SCROW >( maSize.mnRows, aNewRange.aEnd.Row() + 1 );
2412 }
2413 
2415 {
2416  if( mbRowOn )
2417  ImplRowOff();
2418  mxRowItemSet.reset( new SfxItemSet( maTableItemSet ) );
2419  maCurrCell.mnCol = 0;
2420  mbRowOn = true;
2421  mbDataOn = false;
2422 }
2423 
2425 {
2426  if( mbDataOn )
2427  ImplDataOff();
2428  if( mbRowOn )
2429  {
2430  mxRowItemSet.reset();
2431  ++maCurrCell.mnRow;
2432  mbRowOn = mbDataOn = false;
2433  }
2434 }
2435 
2436 void ScHTMLTable::ImplDataOn( const ScHTMLSize& rSpanSize )
2437 {
2438  if( mbDataOn )
2439  ImplDataOff();
2440  if( !mbRowOn )
2441  ImplRowOn();
2442  mxDataItemSet.reset( new SfxItemSet( *mxRowItemSet ) );
2443  InsertNewCell( rSpanSize );
2444  mbDataOn = true;
2445  mbPushEmptyLine = false;
2446 }
2447 
2449 {
2450  if( mbDataOn )
2451  {
2452  mxDataItemSet.reset();
2453  ++maCurrCell.mnCol;
2454  mpCurrEntryVector = nullptr;
2455  mbDataOn = false;
2456  }
2457 }
2458 
2460 {
2461  // special handling for table header cells
2462  if( rInfo.nToken == HtmlTokenId::TABLEHEADER_ON )
2463  {
2465  rItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
2466  }
2467 
2468  const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2469  for (const auto& rOption : rOptions)
2470  {
2471  switch( rOption.GetToken() )
2472  {
2473  case HtmlOptionId::ALIGN:
2474  {
2475  SvxCellHorJustify eVal = SvxCellHorJustify::Standard;
2476  const OUString& rOptVal = rOption.GetString();
2477  if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
2478  eVal = SvxCellHorJustify::Right;
2479  else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
2480  eVal = SvxCellHorJustify::Center;
2481  else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
2482  eVal = SvxCellHorJustify::Left;
2483  if( eVal != SvxCellHorJustify::Standard )
2484  rItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY ) );
2485  }
2486  break;
2487 
2488  case HtmlOptionId::VALIGN:
2489  {
2490  SvxCellVerJustify eVal = SvxCellVerJustify::Standard;
2491  const OUString& rOptVal = rOption.GetString();
2492  if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
2493  eVal = SvxCellVerJustify::Top;
2494  else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
2495  eVal = SvxCellVerJustify::Center;
2496  else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
2497  eVal = SvxCellVerJustify::Bottom;
2498  if( eVal != SvxCellVerJustify::Standard )
2499  rItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY ) );
2500  }
2501  break;
2502 
2503  case HtmlOptionId::BGCOLOR:
2504  {
2505  Color aColor;
2506  rOption.GetColor( aColor );
2507  rItemSet.Put( SvxBrushItem( aColor, ATTR_BACKGROUND ) );
2508  }
2509  break;
2510  default: break;
2511  }
2512  }
2513 }
2514 
2515 void ScHTMLTable::SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize )
2516 {
2517  OSL_ENSURE( nCellPos >= 0, "ScHTMLTable::SetDocSize - unexpected negative position" );
2518  ScSizeVec& rSizes = maCumSizes[ eOrient ];
2519  size_t nIndex = static_cast< size_t >( nCellPos );
2520  // expand with height/width == 1
2521  while( nIndex >= rSizes.size() )
2522  rSizes.push_back( rSizes.empty() ? 1 : (rSizes.back() + 1) );
2523  // update size of passed position and all following
2524  // #i109987# only grow, don't shrink - use the largest needed size
2525  SCCOLROW nDiff = nSize - ((nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]));
2526  if( nDiff > 0 )
2527  std::for_each(rSizes.begin() + nIndex, rSizes.end(), [&nDiff](SCCOLROW& rSize) { rSize += nDiff; });
2528 }
2529 
2531  ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nCellSpan, SCCOLROW nRealDocSize )
2532 {
2533  SCCOLROW nDiffSize = 0;
2534  // in merged columns/rows: reduce needed size by size of leading columns
2535  while( nCellSpan > 1 )
2536  {
2537  nDiffSize += GetDocSize( eOrient, nCellPos );
2538  --nCellSpan;
2539  ++nCellPos;
2540  }
2541  // set remaining needed size to last column/row
2542  nRealDocSize -= std::min< SCCOLROW >( nRealDocSize - 1, nDiffSize );
2543  SetDocSize( eOrient, nCellPos, nRealDocSize );
2544 }
2545 
2547 {
2548  for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2549  aIter->FillEmptyCells();
2550 
2551  // insert the final vertically merged ranges into maUsedCells
2552  for ( size_t i = 0, nRanges = maVMergedCells.size(); i < nRanges; ++i )
2553  {
2554  ScRange & rRange = maVMergedCells[ i ];
2555  maUsedCells.Join( rRange );
2556  }
2557 
2558  for( ScAddress aAddr; aAddr.Row() < maSize.mnRows; aAddr.IncRow() )
2559  {
2560  for( aAddr.SetCol( 0 ); aAddr.Col() < maSize.mnCols; aAddr.IncCol() )
2561  {
2562  if( !maUsedCells.Find( aAddr ) )
2563  {
2564  // create a range for the lock list (used to calc. cell span)
2565  ScRange aRange( aAddr );
2566  do
2567  {
2568  aRange.aEnd.IncCol();
2569  }
2570  while( (aRange.aEnd.Col() < maSize.mnCols) && !maUsedCells.Find( aRange.aEnd ) );
2571  aRange.aEnd.IncCol( -1 );
2572  maUsedCells.Join( aRange );
2573 
2574  // insert a dummy entry
2575  ScHTMLEntryPtr xEntry = CreateEntry();
2576  ImplPushEntryToVector( maEntryMap[ ScHTMLPos( aAddr ) ], xEntry );
2577  }
2578  }
2579  }
2580 }
2581 
2583 {
2584  // recalc table sizes recursively from inner to outer
2585  for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2586  aIter->RecalcDocSize();
2587 
2588  /* Two passes: first calculates the sizes of single columns/rows, then
2589  the sizes of spanned columns/rows. This allows to fill nested tables
2590  into merged cells optimally. */
2591  static const sal_uInt16 PASS_SINGLE = 0;
2592  static const sal_uInt16 PASS_SPANNED = 1;
2593  for( sal_uInt16 nPass = PASS_SINGLE; nPass <= PASS_SPANNED; ++nPass )
2594  {
2595  // iterate through every table cell
2596  for( const auto& [rCellPos, rEntryVector] : maEntryMap )
2597  {
2598  ScHTMLSize aCellSpan = GetSpan( rCellPos );
2599 
2600  // process the dimension of the current cell in this pass?
2601  // (pass is single and span is 1) or (pass is not single and span is not 1)
2602  bool bProcessColWidth = ((nPass == PASS_SINGLE) == (aCellSpan.mnCols == 1));
2603  bool bProcessRowHeight = ((nPass == PASS_SINGLE) == (aCellSpan.mnRows == 1));
2604  if( bProcessColWidth || bProcessRowHeight )
2605  {
2606  ScHTMLSize aDocSize( 1, 0 ); // resulting size of the cell in document
2607 
2608  // expand the cell size for each cell parse entry
2609  for( const auto& rpEntry : rEntryVector )
2610  {
2611  ScHTMLTable* pTable = GetExistingTable( rpEntry->GetTableId() );
2612  // find entry with maximum width
2613  if( bProcessColWidth && pTable )
2614  aDocSize.mnCols = std::max( aDocSize.mnCols, static_cast< SCCOL >( pTable->GetDocSize( tdCol ) ) );
2615  // add up height of each entry
2616  if( bProcessRowHeight )
2617  aDocSize.mnRows += pTable ? pTable->GetDocSize( tdRow ) : 1;
2618  }
2619  if( !aDocSize.mnRows )
2620  aDocSize.mnRows = 1;
2621 
2622  if( bProcessColWidth )
2623  CalcNeededDocSize( tdCol, rCellPos.mnCol, aCellSpan.mnCols, aDocSize.mnCols );
2624  if( bProcessRowHeight )
2625  CalcNeededDocSize( tdRow, rCellPos.mnRow, aCellSpan.mnRows, aDocSize.mnRows );
2626  }
2627  }
2628  }
2629 }
2630 
2631 void ScHTMLTable::RecalcDocPos( const ScHTMLPos& rBasePos )
2632 {
2633  maDocBasePos = rBasePos;
2634  // after the previous assignment it is allowed to call GetDocPos() methods
2635 
2636  // iterate through every table cell
2637  for( auto& [rCellPos, rEntryVector] : maEntryMap )
2638  {
2639  // fixed doc position of the entire cell (first entry)
2640  const ScHTMLPos aCellDocPos( GetDocPos( rCellPos ) );
2641  // fixed doc size of the entire cell
2642  const ScHTMLSize aCellDocSize( GetDocSize( rCellPos ) );
2643 
2644  // running doc position for single entries
2645  ScHTMLPos aEntryDocPos( aCellDocPos );
2646 
2647  ScHTMLEntry* pEntry = nullptr;
2648  for( const auto& rpEntry : rEntryVector )
2649  {
2650  pEntry = rpEntry;
2651  if( ScHTMLTable* pTable = GetExistingTable( pEntry->GetTableId() ) )
2652  {
2653  pTable->RecalcDocPos( aEntryDocPos ); // recalc nested table
2654  pEntry->nCol = SCCOL_MAX;
2655  pEntry->nRow = SCROW_MAX;
2656  SCROW nTableRows = static_cast< SCROW >( pTable->GetDocSize( tdRow ) );
2657 
2658  // use this entry to pad empty space right of table
2659  if( mpParentTable ) // ... but not in global table
2660  {
2661  SCCOL nStartCol = aEntryDocPos.mnCol + static_cast< SCCOL >( pTable->GetDocSize( tdCol ) );
2662  SCCOL nNextCol = aEntryDocPos.mnCol + aCellDocSize.mnCols;
2663  if( nStartCol < nNextCol )
2664  {
2665  pEntry->nCol = nStartCol;
2666  pEntry->nRow = aEntryDocPos.mnRow;
2667  pEntry->nColOverlap = nNextCol - nStartCol;
2668  pEntry->nRowOverlap = nTableRows;
2669  }
2670  }
2671  aEntryDocPos.mnRow += nTableRows;
2672  }
2673  else
2674  {
2675  pEntry->nCol = aEntryDocPos.mnCol;
2676  pEntry->nRow = aEntryDocPos.mnRow;
2677  if( mpParentTable ) // do not merge in global table
2678  pEntry->nColOverlap = aCellDocSize.mnCols;
2679  ++aEntryDocPos.mnRow;
2680  }
2681  }
2682 
2683  // pEntry points now to last entry.
2684  if( pEntry )
2685  {
2686  if( (pEntry == rEntryVector.front()) && (pEntry->GetTableId() == SC_HTML_NO_TABLE) )
2687  {
2688  // pEntry is the only entry in this cell - merge rows of cell with single non-table entry.
2689  pEntry->nRowOverlap = aCellDocSize.mnRows;
2690  }
2691  else
2692  {
2693  // fill up incomplete entry lists
2694  SCROW nFirstUnusedRow = aCellDocPos.mnRow + aCellDocSize.mnRows;
2695  while( aEntryDocPos.mnRow < nFirstUnusedRow )
2696  {
2697  ScHTMLEntryPtr xDummyEntry( new ScHTMLEntry( pEntry->GetItemSet() ) );
2698  xDummyEntry->nCol = aEntryDocPos.mnCol;
2699  xDummyEntry->nRow = aEntryDocPos.mnRow;
2700  xDummyEntry->nColOverlap = aCellDocSize.mnCols;
2701  ImplPushEntryToVector( rEntryVector, xDummyEntry );
2702  ++aEntryDocPos.mnRow;
2703  }
2704  }
2705  }
2706  }
2707 }
2708 
2710  SfxItemPool& rPool,
2711  EditEngine& rEditEngine,
2712  std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseVector,
2713  ScHTMLTableId& rnUnusedId,
2714  ScHTMLParser* pParser
2715 ) :
2716  ScHTMLTable( rPool, rEditEngine, rEEParseVector, rnUnusedId, pParser )
2717 {
2718 }
2719 
2721 {
2722 }
2723 
2725 {
2726  // Fills up empty cells with a dummy entry. */
2727  FillEmptyCells();
2728  // recalc table sizes of all nested tables and this table
2729  RecalcDocSize();
2730  // recalc document positions of all entries in this table and in nested tables
2731  RecalcDocPos( GetDocPos() );
2732 }
2733 
2735  ScHTMLParser( pEditEngine, pDoc ),
2736  mnUnusedId( SC_HTML_GLOBAL_TABLE ),
2737  mbTitleOn( false )
2738 {
2739  mxGlobTable.reset(
2740  new ScHTMLGlobalTable(*pPool, *pEdit, maList, mnUnusedId, this));
2741  mpCurrTable = mxGlobTable.get();
2742 }
2743 
2745 {
2746 }
2747 
2748 ErrCode ScHTMLQueryParser::Read( SvStream& rStrm, const OUString& rBaseURL )
2749 {
2750  SvKeyValueIteratorRef xValues;
2751  SvKeyValueIterator* pAttributes = nullptr;
2752 
2753  SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
2754  if( pObjSh && pObjSh->IsLoading() )
2755  {
2756  pAttributes = pObjSh->GetHeaderAttributes();
2757  }
2758  else
2759  {
2760  /* When not loading, set up fake HTTP headers to force the SfxHTMLParser
2761  to use UTF8 (used when pasting from clipboard) */
2762  const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
2763  if( pCharSet )
2764  {
2765  OUString aContentType = "text/html; charset=" +
2766  OUString::createFromAscii( pCharSet );
2767 
2768  xValues = new SvKeyValueIterator;
2769  xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
2770  pAttributes = xValues.get();
2771  }
2772  }
2773 
2775  pEdit->SetHtmlImportHdl( LINK( this, ScHTMLQueryParser, HTMLImportHdl ) );
2776  ErrCode nErr = pEdit->Read( rStrm, rBaseURL, EETextFormat::Html, pAttributes );
2777  pEdit->SetHtmlImportHdl( aOldLink );
2778 
2779  mxGlobTable->Recalc();
2780  nColMax = static_cast< SCCOL >( mxGlobTable->GetDocSize( tdCol ) - 1 );
2781  nRowMax = static_cast< SCROW >( mxGlobTable->GetDocSize( tdRow ) - 1 );
2782 
2783  return nErr;
2784 }
2785 
2787 {
2788  return mxGlobTable.get();
2789 }
2790 
2792 {
2793  switch( rInfo.nToken )
2794  {
2795 // --- meta data ---
2796  case HtmlTokenId::META: MetaOn( rInfo ); break; // <meta>
2797 
2798 // --- title handling ---
2799  case HtmlTokenId::TITLE_ON: TitleOn(); break; // <title>
2800  case HtmlTokenId::TITLE_OFF: TitleOff( rInfo ); break; // </title>
2801 
2802  case HtmlTokenId::STYLE_ON: break;
2803  case HtmlTokenId::STYLE_OFF: ParseStyle(rInfo.aText); break;
2804 
2805 // --- body handling ---
2806  case HtmlTokenId::BODY_ON: mpCurrTable->BodyOn( rInfo ); break; // <body>
2807  case HtmlTokenId::BODY_OFF: mpCurrTable->BodyOff( rInfo ); break; // </body>
2808 
2809 // --- insert text ---
2810  case HtmlTokenId::TEXTTOKEN: InsertText( rInfo ); break; // any text
2811  case HtmlTokenId::LINEBREAK: mpCurrTable->BreakOn(); break; // <br>
2812  case HtmlTokenId::HEAD1_ON: // <h1>
2813  case HtmlTokenId::HEAD2_ON: // <h2>
2814  case HtmlTokenId::HEAD3_ON: // <h3>
2815  case HtmlTokenId::HEAD4_ON: // <h4>
2816  case HtmlTokenId::HEAD5_ON: // <h5>
2817  case HtmlTokenId::HEAD6_ON: // <h6>
2818  case HtmlTokenId::PARABREAK_ON: mpCurrTable->HeadingOn(); break; // <p>
2819 
2820 // --- misc. contents ---
2821  case HtmlTokenId::ANCHOR_ON: mpCurrTable->AnchorOn(); break; // <a>
2822 
2823 // --- table handling ---
2824  case HtmlTokenId::TABLE_ON: TableOn( rInfo ); break; // <table>
2825  case HtmlTokenId::TABLE_OFF: TableOff( rInfo ); break; // </table>
2826  case HtmlTokenId::TABLEROW_ON: mpCurrTable->RowOn( rInfo ); break; // <tr>
2827  case HtmlTokenId::TABLEROW_OFF: mpCurrTable->RowOff( rInfo ); break; // </tr>
2828  case HtmlTokenId::TABLEHEADER_ON: // <th>
2829  case HtmlTokenId::TABLEDATA_ON: mpCurrTable->DataOn( rInfo ); break; // <td>
2830  case HtmlTokenId::TABLEHEADER_OFF: // </th>
2831  case HtmlTokenId::TABLEDATA_OFF: mpCurrTable->DataOff( rInfo ); break; // </td>
2832  case HtmlTokenId::PREFORMTXT_ON: PreOn( rInfo ); break; // <pre>
2833  case HtmlTokenId::PREFORMTXT_OFF: PreOff( rInfo ); break; // </pre>
2834 
2835 // --- formatting ---
2836  case HtmlTokenId::FONT_ON: FontOn( rInfo ); break; // <font>
2837 
2838  case HtmlTokenId::BIGPRINT_ON: // <big>
2841  break;
2842  case HtmlTokenId::SMALLPRINT_ON: // <small>
2845  break;
2846 
2847  case HtmlTokenId::BOLD_ON: // <b>
2848  case HtmlTokenId::STRONG_ON: // <strong>
2850  break;
2851 
2852  case HtmlTokenId::ITALIC_ON: // <i>
2853  case HtmlTokenId::EMPHASIS_ON: // <em>
2854  case HtmlTokenId::ADDRESS_ON: // <address>
2855  case HtmlTokenId::BLOCKQUOTE_ON: // <blockquote>
2856  case HtmlTokenId::BLOCKQUOTE30_ON: // <bq>
2857  case HtmlTokenId::CITATION_ON: // <cite>
2858  case HtmlTokenId::VARIABLE_ON: // <var>
2860  break;
2861 
2862  case HtmlTokenId::DEFINSTANCE_ON: // <dfn>
2865  break;
2866 
2867  case HtmlTokenId::UNDERLINE_ON: // <u>
2869  break;
2870  default: break;
2871  }
2872 }
2873 
2875 {
2876  mpCurrTable->PutText( rInfo );
2877  if( mbTitleOn )
2878  maTitle.append(rInfo.aText);
2879 }
2880 
2882 {
2883  const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2884  for (const auto& rOption : rOptions)
2885  {
2886  switch( rOption.GetToken() )
2887  {
2888  case HtmlOptionId::FACE :
2889  {
2890  const OUString& rFace = rOption.GetString();
2891  OUString aFontName;
2892  sal_Int32 nPos = 0;
2893  while( nPos != -1 )
2894  {
2895  // font list separator: VCL = ';' HTML = ','
2896  OUString aFName = comphelper::string::strip(rFace.getToken(0, ',', nPos), ' ');
2897  aFontName = ScGlobal::addToken(aFontName, aFName, ';');
2898  }
2899  if ( !aFontName.isEmpty() )
2901  aFontName, EMPTY_OUSTRING, PITCH_DONTKNOW,
2902  RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
2903  }
2904  break;
2905  case HtmlOptionId::SIZE :
2906  {
2907  sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( rOption.GetNumber(), 1, SC_HTML_FONTSIZES );
2909  }
2910  break;
2911  case HtmlOptionId::COLOR :
2912  {
2913  Color aColor;
2914  rOption.GetColor( aColor );
2916  }
2917  break;
2918  default: break;
2919  }
2920  }
2921 }
2922 
2924 {
2925  if( mpDoc->GetDocumentShell() )
2926  {
2927  HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
2928 
2929  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
2930  mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
2931  pParser->ParseMetaOptions(
2932  xDPS->getDocumentProperties(),
2934  }
2935 }
2936 
2938 {
2939  mbTitleOn = true;
2940  maTitle.setLength(0);
2941 }
2942 
2944 {
2945  if( !mbTitleOn )
2946  return;
2947 
2948  OUString aTitle = maTitle.makeStringAndClear().trim();
2949  if (!aTitle.isEmpty() && mpDoc->GetDocumentShell())
2950  {
2951  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
2952  mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
2953 
2954  xDPS->getDocumentProperties()->setTitle(aTitle);
2955  }
2956  InsertText( rInfo );
2957  mbTitleOn = false;
2958 }
2959 
2961 {
2962  mpCurrTable = mpCurrTable->TableOn( rInfo );
2963 }
2964 
2966 {
2967  mpCurrTable = mpCurrTable->TableOff( rInfo );
2968 }
2969 
2971 {
2972  mpCurrTable = mpCurrTable->PreOn( rInfo );
2973 }
2974 
2976 {
2977  mpCurrTable = mpCurrTable->PreOff( rInfo );
2978 }
2979 
2981 {
2982  mpCurrTable = mpCurrTable->CloseTable( rInfo );
2983 }
2984 
2985 namespace {
2986 
2990 class CSSHandler
2991 {
2992  struct MemStr
2993  {
2994  const char* mp;
2995  size_t mn;
2996 
2997  MemStr() : mp(nullptr), mn(0) {}
2998  MemStr(const char* p, size_t n) : mp(p), mn(n) {}
2999  MemStr& operator=(const MemStr& r) = default;
3000  };
3001 
3002  MemStr maPropName;
3003  MemStr maPropValue;
3004 
3005 public:
3006  explicit CSSHandler() {}
3007 
3008  static void at_rule_name(const char* /*p*/, size_t /*n*/)
3009  {
3010  // TODO: For now, we ignore at-rule properties
3011  }
3012 
3013  void property_name(const char* p, size_t n)
3014  {
3015  maPropName = MemStr(p, n);
3016  }
3017 
3018  void value(const char* p, size_t n)
3019  {
3020  maPropValue = MemStr(p, n);
3021  }
3022 
3023  static void begin_parse() {}
3024 
3025  static void end_parse() {}
3026 
3027  static void begin_block() {}
3028 
3029  static void end_block() {}
3030 
3031  static void begin_property() {}
3032 
3033  void end_property()
3034  {
3035  maPropName = MemStr();
3036  maPropValue = MemStr();
3037  }
3038 
3039  // new members
3040  static void simple_selector_type(const char* /*p*/, size_t /*n*/) {}
3041 
3042  static void simple_selector_class(const char* /*p*/, size_t /*n*/) {}
3043 
3044  static void simple_selector_pseudo_element(orcus::css::pseudo_element_t /*pe*/) {}
3045 
3046  static void simple_selector_pseudo_class(orcus::css::pseudo_class_t /*pc*/) {}
3047 
3048  static void simple_selector_id(const char* /*p*/, size_t /*n*/) {}
3049 
3050  static void end_simple_selector() {}
3051 
3052  static void end_selector() {}
3053 
3054  static void combinator(orcus::css::combinator_t /*combinator*/) {}
3055 
3056  static void rgb(uint8_t /*red*/ , uint8_t /*green*/ , uint8_t /*blue*/ ) {}
3057 
3058  static void rgba(uint8_t /*red*/ , uint8_t /*green*/ , uint8_t /*blue*/ , double /*alpha*/ ) {}
3059 
3060  static void hsl(uint8_t /*hue*/ , uint8_t /*sat*/ , uint8_t /*light*/ ) {}
3061 
3062  static void hsla(uint8_t /*hue*/ , uint8_t /*sat*/ , uint8_t /*light*/ , double /*alpha*/ ) {}
3063 
3064  static void url(const char* /*p*/, size_t /*n*/) {}
3065 
3066 };
3067 
3068 }
3069 
3070 void ScHTMLQueryParser::ParseStyle(std::u16string_view rStrm)
3071 {
3072  OString aStr = OUStringToOString(rStrm, RTL_TEXTENCODING_UTF8);
3073  CSSHandler aHdl;
3074  orcus::css_parser<CSSHandler> aParser(aStr.getStr(), aStr.getLength(), aHdl);
3075  try
3076  {
3077  aParser.parse();
3078  }
3079  catch (const orcus::css::parse_error&)
3080  {
3081  // TODO: Parsing of CSS failed. Do nothing for now.
3082  }
3083 }
3084 
3085 IMPL_LINK( ScHTMLQueryParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
3086 {
3087  switch( rInfo.eState )
3088  {
3089  case HtmlImportState::Start:
3090  break;
3091 
3092  case HtmlImportState::NextToken:
3093  ProcessToken( rInfo );
3094  break;
3095 
3096  case HtmlImportState::InsertPara:
3097  mpCurrTable->InsertPara( rInfo );
3098  break;
3099 
3100  case HtmlImportState::SetAttr:
3101  case HtmlImportState::InsertText:
3102  case HtmlImportState::InsertField:
3103  break;
3104 
3105  case HtmlImportState::End:
3106  while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
3107  CloseTable( rInfo );
3108  break;
3109 
3110  default:
3111  OSL_FAIL( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
3112  }
3113 }
3114 
3115 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::unique_ptr< OuterMap > pTables
Definition: htmlpars.hxx:159
void RowOn(const HtmlImportInfo &rInfo)
Starts next row (tag).
Definition: htmlpars.cxx:1964
EditEngine * pEdit
Definition: eeparser.hxx:101
EditEngine & mrEditEngine
List of all used cells.
Definition: htmlpars.hxx:527
Type
SvxCellHorJustify
ScHTMLStyles()
just a persistent empty string.
Definition: htmlpars.cxx:70
std::vector< std::shared_ptr< ScEEParseEntry > > & mrEEParseList
Edit engine (from ScEEParser).
Definition: htmlpars.hxx:528
SCCOL mnCols
Definition: htmlpars.hxx:261
ScHTMLTable * PreOn(const HtmlImportInfo &rInfo)
Starts a new table based on preformatted text (.
Definition: htmlpars.cxx:1953
sal_Int32 nStartPara
SvKeyValueIterator * GetHeaderAttributes()
const Value & back() const
void AnchorOn(HtmlImportInfo *)
Definition: htmlpars.cxx:1376
SfxItemSet maTableItemSet
Unique identifier of this table.
Definition: htmlpars.hxx:521
const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_LARGE
Definition: htmlpars.hxx:37
sal_Int32 nIndex
const char nHorizontal
Definition: eeparser.hxx:33
ScAddress aStart
Definition: address.hxx:499
static void EntryEnd(ScEEParseEntry *, const ESelection &)
Definition: htmlpars.cxx:310
#define OOO_STRING_SVTOOLS_HTML_VA_bottom
ScHTMLSize maSize
Cumulated cell counts for each HTML table column/row.
Definition: htmlpars.hxx:533
ScDocument * mpDoc
Definition: htmlpars.hxx:81
static void MakeCol(ScHTMLColOffset *, sal_uInt16 &nOffset, sal_uInt16 &nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol)
Definition: htmlpars.cxx:362
void SetHtmlImportHdl(const Link< HtmlImportInfo &, void > &rLink)
#define EMPTY_OUSTRING
Definition: global.hxx:213
ScHTMLTableAutoId(ScHTMLTableId &rnUnusedId)
Reference to global unused identifier variable.
Definition: htmlpars.cxx:1787
static void ModifyOffset(ScHTMLColOffset *, sal_uInt16 &nOldOffset, sal_uInt16 &nNewOffset, sal_uInt16 nOffsetTol)
Definition: htmlpars.cxx:396
A map of ScHTMLTable objects.
Definition: htmlpars.cxx:1679
FormulaCommand pE
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
virtual ~ScHTMLParser() override
Definition: htmlpars.cxx:194
void Strip(const EditEngine &rEditEngine)
Deletes leading and trailing empty paragraphs from the entry.
Definition: htmlpars.cxx:1656
SCROW Row() const
Definition: address.hxx:261
bool mbBorderOn
Definition: htmlpars.hxx:537
void setWidth(tools::Long nWidth)
OUString aText
SCCOL nColCnt
Definition: eeparser.hxx:108
A single entry containing a line of text or representing a table.
Definition: htmlpars.hxx:271
#define OOO_STRING_SVTOOLS_HTML_AL_center
bool PushEntry(ScHTMLEntryPtr &rxEntry)
Tries to insert the entry into the current cell.
Definition: htmlpars.cxx:2282
void NewActEntry(const ScEEParseEntry *)
Definition: eeimpars.cxx:644
ScHTMLTableStdMap::const_iterator const_iterator
Definition: htmlpars.cxx:1687
const sal_uInt32 SC_HTML_FONTSIZES
Definition: htmlpars.hxx:33
ScHTMLEntry(const SfxItemSet &rItemSet, ScHTMLTableId nTableId=SC_HTML_NO_TABLE)
Definition: htmlpars.cxx:1620
OString strip(std::string_view rIn, char c)
ESelection aSel
Definition: eeparser.hxx:54
void Colonize(ScEEParseEntry *)
Definition: htmlpars.cxx:774
::std::map< SCROW, SCROW > InnerMap
Definition: htmlpars.hxx:144
SC_DLLPUBLIC bool Intersects(const ScRange &rRange) const
Definition: address.cxx:1558
bool IsEmptyCell() const
Returns true, if the current cell does not contain an entry yet.
Definition: htmlpars.cxx:2252
void InsertNewCell(const ScHTMLSize &rSpanSize)
Inserts a new cell in an unused position, starting from current cell position.
Definition: htmlpars.cxx:2361
::std::map< sal_uInt16, InnerMap * > OuterMap
Definition: htmlpars.hxx:148
OUString maTableName
Table of nested HTML tables.
Definition: htmlpars.hxx:519
ScRangeList maHMergedCells
Items for the current cell.
Definition: htmlpars.hxx:524
sal_uIntPtr sal_uLong
std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap
Definition: htmlpars.cxx:1683
long Long
void CloseEntry(const HtmlImportInfo *)
Definition: htmlpars.cxx:802
FAMILY_DONTKNOW
void AdjustEnd(const HtmlImportInfo &rInfo)
Sets end point of the entry selection to the end of the import info object.
Definition: htmlpars.cxx:1646
bool IsAtBeginningOfText(const HtmlImportInfo *)
Definition: htmlpars.cxx:1386
ScHTMLTable * TableOn(const HtmlImportInfo &rInfo)
Starts a new table nested in this table (.
Definition: htmlpars.cxx:1942
OUString GetImportFormatName(sal_uInt16 nFormat)
ScHTMLTable * TableOff(const HtmlImportInfo &rInfo)
Closes this table (tag).
Definition: htmlpars.cxx:1948
const SCCOL SCCOL_MAX
Definition: address.hxx:56
NamePropsType m_GlobalProps
Definition: htmlpars.hxx:53
bool mbPreFormText
true = Table borders on.
Definition: htmlpars.hxx:538
sal_Int64 n
const OUString & GetString() const
SCROW nRowMax
Definition: eeparser.hxx:111
aBuf
virtual ~ScHTMLTable()
Definition: htmlpars.cxx:1862
ScSizeVec maCumSizes[2]
Working entry, not yet inserted in a list.
Definition: htmlpars.hxx:532
void InsertText(const HtmlImportInfo &rInfo)
Inserts a text portion into current entry.
Definition: htmlpars.cxx:2874
const_iterator find(const Value &x) const
void CloseTable(const HtmlImportInfo &rInfo)
Closes the current table, regardless on opening tag.
Definition: htmlpars.cxx:2980
ScAddress aEnd
Definition: address.hxx:500
SCCOLROW GetDocSize(ScHTMLOrient eOrient, SCCOLROW nCellPos) const
Returns the resulting document row/column count of the specified HTML row/column. ...
Definition: htmlpars.cxx:2149
void ProcToken(HtmlImportInfo *)
Definition: htmlpars.cxx:1449
constexpr TypedWhichId< SvxFontItem > ATTR_FONT(100)
SCROW mnRow
Definition: htmlpars.hxx:236
css::uno::Reference< css::frame::XModel > GetModel() const
virtual ~ScHTMLGlobalTable() override
Definition: htmlpars.cxx:2720
ScHTMLEntryPtr mxCurrEntry
Current entry vector from map for faster access.
Definition: htmlpars.hxx:531
ScHTMLTableId & mrnUnusedId
The created unique table identifier.
Definition: htmlpars.hxx:309
SotClipboardFormatId & operator++(SotClipboardFormatId &eFormat)
SC_DLLPUBLIC void ApplyAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem &rAttr)
Definition: document.cxx:4807
ScDocument & GetDoc()
Definition: htmlpars.hxx:90
const SCROW SCROW_MAX
Definition: address.hxx:55
ScHTMLParser * mpParser
Resulting base address in a Calc document.
Definition: htmlpars.hxx:536
static void ParseStyle(std::u16string_view rStrm)
Definition: htmlpars.cxx:3070
void NextRow(const HtmlImportInfo *)
Definition: htmlpars.cxx:326
void BodyOff(const HtmlImportInfo &rInfo)
Closes the body of the HTML document ( tag).
Definition: htmlpars.cxx:2122
ScHTMLEntryVector * mpCurrEntryVector
List of entries for each cell.
Definition: htmlpars.hxx:530
const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_SMALL
Definition: htmlpars.hxx:36
virtual ~ScHTMLQueryParser() override
Definition: htmlpars.cxx:2744
bool mbDataOn
true = Inside of .
Definition: htmlpars.hxx:540
ScHTMLQueryParser(EditEngine *pEditEngine, ScDocument *pDoc)
Definition: htmlpars.cxx:2734
void FillEmptyCells()
Fills all empty cells in this and nested tables with dummy parse entries.
Definition: htmlpars.cxx:2546
const Value & front() const
void TableRowOn(const HtmlImportInfo *)
Definition: htmlpars.cxx:977
static OutputDevice * GetDefaultDevice()
WEIGHT_BOLD
constexpr tools::Long Width() const
const ScHTMLTableId SC_HTML_NO_TABLE
Used as table index for normal (non-table) entries in ScHTMLEntry structs.
Definition: htmlpars.hxx:230
SvParser< HtmlTokenId > * pParser
sal_uInt16 nTableWidth
Definition: htmlpars.hxx:168
static void MakeColNoRef(ScHTMLColOffset *, sal_uInt16 nOffset, sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol)
Definition: htmlpars.cxx:380
void MetaOn(const HtmlImportInfo &rInfo)
Processes the tag.
Definition: htmlpars.cxx:2923
std::map< ScHTMLPos, ScHTMLEntryVector > maEntryMap
List that owns the parse entries (from ScEEParser).
Definition: htmlpars.hxx:529
virtual const ScHTMLTable * GetGlobalTable() const override
Returns the "global table" which contains the entire HTML document.
Definition: htmlpars.cxx:290
sal_uInt16 nTable
Definition: htmlpars.hxx:164
sal_uInt16 sal_Unicode
sal_uInt16 ScHTMLTableId
Type for a unique identifier for each table.
Definition: htmlpars.hxx:226
ScHTMLTable & mrParentTable
Definition: htmlpars.cxx:1690
void TableRowOff(const HtmlImportInfo *)
Definition: htmlpars.cxx:984
ScRangeList maUsedCells
List of all vertically merged cells.
Definition: htmlpars.hxx:526
SvxCellVerJustify
void RecalcDocSize()
Recalculates the size of all columns/rows in the table, regarding nested tables.
Definition: htmlpars.cxx:2582
sal_uInt16 nTab
Definition: eeparser.hxx:65
ScHTMLLayoutParser(EditEngine *, const OUString &rBaseURL, const Size &aPageSize, ScDocument *)
Definition: htmlpars.cxx:198
ScHTMLTable * CreateTable(const HtmlImportInfo &rInfo, bool bPreFormText)
Inserts a new table into the container.
Definition: htmlpars.cxx:1742
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
ScHTMLGlobalTablePtr mxGlobTable
The title of the document.
Definition: htmlpars.hxx:611
SCROW nRowCnt
Definition: eeparser.hxx:109
constexpr sal_uInt32 NUMBERFORMAT_ENTRY_NOT_FOUND
o3tl::sorted_vector< sal_uLong > ScHTMLColOffset
Definition: htmlpars.hxx:96
bool bEntirePara
Definition: eeparser.hxx:72
int nCount
void Recalc()
Recalculates sizes and resulting positions of all document entries.
Definition: htmlpars.cxx:2724
const OUString & getPropertyValue(const OUString &rElem, const OUString &rClass, const OUString &rPropName) const
Find best-matching property value for given element and class names.
Definition: htmlpars.cxx:117
static OUString GetAbsURL(OUString const &rTheBaseURIRef, OUString const &rTheRelURIRef, EncodeMechanism eEncodeMechanism=EncodeMechanism::WasEncoded, DecodeMechanism eDecodeMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
ScHTMLTable * CloseTable(const HtmlImportInfo &rInfo)
Closes this table (tag) or preformatted text ( tag).
Definition: htmlpars.cxx:2133
void push_back(const ScRange &rRange)
Definition: rangelst.cxx:1142
::std::vector< ScHTMLEntry * > ScHTMLEntryVector
Definition: htmlpars.hxx:445
ScHTMLTable(ScHTMLTable &rParentTable, const HtmlImportInfo &rInfo, bool bPreFormText)
Creates a new HTML table without content.
Definition: htmlpars.cxx:1794
SCTAB Tab() const
Definition: address.hxx:270
#define OOO_STRING_SVTOOLS_HTML_VA_top
sal_uInt16 nWidth
Definition: eeparser.hxx:70
void SetRow(SCROW nRowP)
Definition: address.hxx:274
#define OOO_STRING_SVTOOLS_HTML_META_content_type
void RecalcDocPos(const ScHTMLPos &rBasePos)
Recalculates the position of all cell entries and nested tables.
Definition: htmlpars.cxx:2631
bool mbImportAlways
Definition: htmlpars.hxx:302
bool mbPushEmptyLine
true = Inside of or .
Definition: htmlpars.hxx:541
::std::stack< std::unique_ptr< ScHTMLTableStackEntry > > aTableStack
Definition: htmlpars.hxx:156
SCROW nRowOverlap
Definition: eeparser.hxx:68
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
sal_Int32 nEndPos
const ScRange * Find(const ScAddress &) const
Definition: rangelst.cxx:1031
size_type size() const
std::unique_ptr< Graphic > pGraphic
Definition: eeparser.hxx:43
void SetCol(SCCOL nColP)
Definition: address.hxx:278
ErrCode Read(SvStream &rInput, const OUString &rBaseURL, EETextFormat, SvKeyValueIterator *pHTTPHeaderAttrs=nullptr)
bool PutEntry(OUString &rString, sal_Int32 &nCheckPos, SvNumFormatType &nType, sal_uInt32 &nKey, LanguageType eLnge=LANGUAGE_DONTKNOW)
::std::map< OUString, std::unique_ptr< PropsType > > NamePropsType
Definition: htmlpars.hxx:50
void HeadingOn()
Inserts a heading line (.
Definition: htmlpars.cxx:1922
const Link< HtmlImportInfo &, void > & GetHtmlImportHdl() const
constexpr TypedWhichId< SvxUnderlineItem > ATTR_FONT_UNDERLINE(104)
sal_uInt16 nOffset
Definition: eeparser.hxx:69
void Image(HtmlImportInfo *)
Definition: htmlpars.cxx:1250
ScHTMLTableId GetTableId() const
Returns the unique identifier of the table.
Definition: htmlpars.hxx:342
static SvxHtmlOptions & Get()
sal_uInt16 nColOffset
Definition: htmlpars.hxx:169
void ImplRowOn()
Set internal states for a new table row.
Definition: htmlpars.cxx:2414
void ImplDataOff()
Set internal states for leaving a table cell.
Definition: htmlpars.cxx:2448
static ErrCode LoadGraphic(const OUString &rPath, const OUString &rFilter, Graphic &rGraphic, GraphicFilter *pFilter=nullptr, sal_uInt16 *pDeterminedFormat=nullptr)
ScHTMLTableMap(ScHTMLTable &rParentTable)
Current table, used for fast search.
Definition: htmlpars.cxx:1715
ScHTMLStyles & GetStyles()
Definition: htmlpars.hxx:89
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:870
constexpr TypedWhichId< SvxPostureItem > ATTR_FONT_POSTURE(103)
T * get() const
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:440
sal_Int32 nEndPara
void ImplPushEntryToVector(ScHTMLEntryVector &rEntryVector, ScHTMLEntryPtr &rxEntry)
Pushes the passed entry into the list of the current cell.
Definition: htmlpars.cxx:2274
ScHTMLEntryPtr CreateEntry() const
Creates and returns a new empty flying entry at position (0,0).
Definition: htmlpars.cxx:2262
void add(const char *pElemName, size_t nElemName, const char *pClassName, size_t nClassName, const OUString &aProp, const OUString &aValue)
Definition: htmlpars.cxx:72
const char nVertical
Definition: eeparser.hxx:34
int i
void RowOff(const HtmlImportInfo &rInfo)
Closes the current row (tag).
Definition: htmlpars.cxx:1975
virtual ~ScHTMLLayoutParser() override
Definition: htmlpars.cxx:225
void PushTableEntry(ScHTMLTableId nTableId)
Pushes a new entry into current cell which references a nested table.
Definition: htmlpars.cxx:2334
void IncCol(SCCOL nDelta=1)
Definition: address.hxx:303
SfxItemSetPtr mxRowItemSet
Items for the entire table.
Definition: htmlpars.hxx:522
sal_uInt32 GetTextLen() const
ScHTMLTable * GetExistingTable(ScHTMLTableId nTableId) const
Tries to find a table from the table container.
Definition: htmlpars.cxx:2344
std::vector< std::shared_ptr< ScEEParseEntry > > maList
Definition: eeparser.hxx:104
sal_Int16 SCCOL
Definition: types.hxx:21
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:873
OUStringBuffer maTitle
Definition: htmlpars.hxx:610
constexpr TypedWhichId< SvxVerJustifyItem > ATTR_VER_JUSTIFY(132)
ScHTMLPos maCurrCell
Size of the table.
Definition: htmlpars.hxx:534
bool mbRowOn
true = Table from preformatted text (
Definition: htmlpars.hxx:539
void DataOff(const HtmlImportInfo &rInfo)
Closes the current cell ( or tag).
Definition: htmlpars.cxx:2099
Point aSpace
Definition: eeparser.hxx:40
void PutItem(const SfxPoolItem &rItem)
Puts the item into the item set of the current entry.
Definition: htmlpars.cxx:1888
void SetCurrTable(ScHTMLTable *pTable) const
Sets a working table with its index for search optimization.
Definition: htmlpars.cxx:1711
ScHTMLTableId mnUnusedId
Pointer to current table (performance).
Definition: htmlpars.hxx:613
sal_uInt16 GetWidthPixel(const HTMLOption &)
Definition: htmlpars.cxx:1356
ColWidthsMap maColWidths
Definition: eeparser.hxx:106
void ProcessToken(const HtmlImportInfo &rInfo)
Handles all possible tags in the HTML document.
Definition: htmlpars.cxx:2791
sal_uInt16 nOffsetTolerance
Definition: htmlpars.hxx:171
ScHTMLColOffset * pLocalColOffset
Definition: htmlpars.hxx:161
ScHTMLTable * mpParentTable
Definition: htmlpars.hxx:517
size_t size() const
Definition: rangelst.hxx:89
#define OOO_STRING_SVTOOLS_HTML_AL_left
static bool IsSpaceCharInfo(const HtmlImportInfo &rInfo)
Returns true, if import info represents a space character.
Definition: htmlpars.cxx:2257
LINESTYLE_SINGLE
The HTML parser for data queries.
Definition: htmlpars.hxx:565
virtual bool ParseMetaOptions(const css::uno::Reference< css::document::XDocumentProperties > &, SvKeyValueIterator *)
Collection of HTML style data parsed from the content of