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