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