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/numformat.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 <numeric>
66 #include <utility>
67 #include <officecfg/Office/Common.hxx>
68 
69 using ::editeng::SvxBorderLine;
70 using namespace ::com::sun::star;
71 
73 
74 void ScHTMLStyles::add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
75  const OUString& aProp, const OUString& aValue)
76 {
77  if (pElemName)
78  {
79  OUString aElem(pElemName, nElemName, RTL_TEXTENCODING_UTF8);
80  aElem = aElem.toAsciiLowerCase();
81  if (pClassName)
82  {
83  // Both element and class names given.
84  ElemsType::iterator itrElem = m_ElemProps.find(aElem);
85  if (itrElem == m_ElemProps.end())
86  {
87  // new element
88  std::pair<ElemsType::iterator, bool> r =
89  m_ElemProps.insert(std::make_pair(aElem, NamePropsType()));
90  if (!r.second)
91  // insertion failed.
92  return;
93  itrElem = r.first;
94  }
95 
96  NamePropsType& rClsProps = itrElem->second;
97  OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
98  aClass = aClass.toAsciiLowerCase();
99  insertProp(rClsProps, aClass, aProp, aValue);
100  }
101  else
102  {
103  // Element name only. Add it to the element global.
104  insertProp(m_ElemGlobalProps, aElem, aProp, aValue);
105  }
106  }
107  else
108  {
109  if (pClassName)
110  {
111  // Class name only. Add it to the global.
112  OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
113  aClass = aClass.toAsciiLowerCase();
114  insertProp(m_GlobalProps, aClass, aProp, aValue);
115  }
116  }
117 }
118 
120  const OUString& rElem, const OUString& rClass, const OUString& rPropName) const
121 {
122  // First, look into the element-class storage.
123  {
124  auto const itr = m_ElemProps.find(rElem);
125  if (itr != m_ElemProps.end())
126  {
127  const NamePropsType& rClasses = itr->second;
128  NamePropsType::const_iterator itr2 = rClasses.find(rClass);
129  if (itr2 != rClasses.end())
130  {
131  const PropsType& rProps = itr2->second;
132  PropsType::const_iterator itr3 = rProps.find(rPropName);
133  if (itr3 != rProps.end())
134  return itr3->second;
135  }
136  }
137  }
138  // Next, look into the class global storage.
139  {
140  auto const itr = m_GlobalProps.find(rClass);
141  if (itr != m_GlobalProps.end())
142  {
143  const PropsType& rProps = itr->second;
144  PropsType::const_iterator itr2 = rProps.find(rPropName);
145  if (itr2 != rProps.end())
146  return itr2->second;
147  }
148  }
149  // As the last resort, look into the element global storage.
150  {
151  auto const itr = m_ElemGlobalProps.find(rClass);
152  if (itr != m_ElemGlobalProps.end())
153  {
154  const PropsType& rProps = itr->second;
155  PropsType::const_iterator itr2 = rProps.find(rPropName);
156  if (itr2 != rProps.end())
157  return itr2->second;
158  }
159  }
160 
161  return maEmpty; // nothing found.
162 }
163 
165  NamePropsType& rStore, const OUString& aName,
166  const OUString& aProp, const OUString& aValue)
167 {
168  NamePropsType::iterator itr = rStore.find(aName);
169  if (itr == rStore.end())
170  {
171  // new element
172  std::pair<NamePropsType::iterator, bool> r =
173  rStore.insert(std::make_pair(aName, PropsType()));
174  if (!r.second)
175  // insertion failed.
176  return;
177 
178  itr = r.first;
179  }
180 
181  PropsType& rProps = itr->second;
182  rProps.emplace(aProp, aValue);
183 }
184 
185 // BASE class for HTML parser classes
186 
188  ScEEParser( pEditEngine ),
189  mpDoc( pDoc )
190 {
191  maFontHeights[0] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_1::get();
192  maFontHeights[1] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_2::get();
193  maFontHeights[2] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_3::get();
194  maFontHeights[3] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_4::get();
195  maFontHeights[4] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_5::get();
196  maFontHeights[5] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_6::get();
197  maFontHeights[6] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_7::get();
198 }
199 
201 {
202 }
203 
205  EditEngine* pEditP, const OUString& rBaseURL, const Size& aPageSizeP,
206  ScDocument* pDocP ) :
207  ScHTMLParser( pEditP, pDocP ),
208  aPageSize( aPageSizeP ),
209  aBaseURL( rBaseURL ),
210  xLockedList( new ScRangeList ),
211  pLocalColOffset( new ScHTMLColOffset ),
212  nFirstTableCell(0),
213  nTableLevel(0),
214  nTable(0),
215  nMaxTable(0),
216  nColCntStart(0),
217  nMaxCol(0),
218  nTableWidth(0),
219  nColOffset(0),
220  nColOffsetStart(0),
221  nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
222  bFirstRow( true ),
223  bTabInTabCell( false ),
224  bInCell( false ),
225  bInTitle( false )
226 {
227  MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
228  MakeColNoRef( &maColOffset, 0, 0, 0, 0 );
229 }
230 
232 {
233  while ( !aTableStack.empty() )
234  {
235  ScHTMLTableStackEntry * pS = aTableStack.top().get();
236  if ( pS->pLocalColOffset != pLocalColOffset )
237  delete pS->pLocalColOffset;
238  aTableStack.pop();
239  }
240  delete pLocalColOffset;
241  if ( pTables )
242  {
243  for( const auto& rEntry : *pTables)
244  delete rEntry.second;
245  pTables.reset();
246  }
247 }
248 
249 ErrCode ScHTMLLayoutParser::Read( SvStream& rStream, const OUString& rBaseURL )
250 {
252  pEdit->SetHtmlImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
253 
255  bool bLoading = pObjSh && pObjSh->IsLoading();
256 
257  SvKeyValueIteratorRef xValues;
258  SvKeyValueIterator* pAttributes = nullptr;
259  if ( bLoading )
260  pAttributes = pObjSh->GetHeaderAttributes();
261  else
262  {
263  // When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
264  // (used when pasting from clipboard)
265  const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
266  if( pCharSet )
267  {
268  OUString aContentType = "text/html; charset=" +
269  OUString::createFromAscii( pCharSet );
270 
271  xValues = new SvKeyValueIterator;
272  xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
273  pAttributes = xValues.get();
274  }
275  }
276 
277  ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Html, pAttributes );
278 
279  pEdit->SetHtmlImportHdl( aOldLink );
280  // Create column width
281  Adjust();
283  sal_uInt16 nCount = maColOffset.size();
284  sal_uLong nOff = maColOffset[0];
285  Size aSize;
286  for ( sal_uInt16 j = 1; j < nCount; j++ )
287  {
288  aSize.setWidth( maColOffset[j] - nOff );
289  aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MapUnit::MapTwip ) );
290  maColWidths[ j-1 ] = aSize.Width();
291  nOff = maColOffset[j];
292  }
293  return nErr;
294 }
295 
297 {
298  return nullptr;
299 }
300 
302 {
304  if ( pE )
305  {
306  if ( !pE->aSel.HasRange() )
307  { // Completely empty, following text ends up in the same paragraph!
308  mxActEntry->aSel.nStartPara = pE->aSel.nEndPara;
309  mxActEntry->aSel.nStartPos = pE->aSel.nEndPos;
310  }
311  }
312  mxActEntry->aSel.nEndPara = mxActEntry->aSel.nStartPara;
313  mxActEntry->aSel.nEndPos = mxActEntry->aSel.nStartPos;
314 }
315 
317 {
318  if ( rSel.nEndPara >= pE->aSel.nStartPara )
319  {
320  pE->aSel.nEndPara = rSel.nEndPara;
321  pE->aSel.nEndPos = rSel.nEndPos;
322  }
323  else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
324  { // Did not attach a paragraph, but empty, do nothing
325  }
326  else
327  {
328  OSL_FAIL( "EntryEnd: EditEngine ESelection End < Start" );
329  }
330 }
331 
333 {
334  if ( bInCell )
335  CloseEntry( pInfo );
336  if ( nRowMax < ++nRowCnt )
337  nRowMax = nRowCnt;
340  bFirstRow = false;
341 }
342 
343 bool ScHTMLLayoutParser::SeekOffset( const ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
344  SCCOL* pCol, sal_uInt16 nOffsetTol )
345 {
346  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
347  ScHTMLColOffset::const_iterator it = pOffset->find( nOffset );
348  bool bFound = it != pOffset->end();
349  sal_uInt16 nPos = it - pOffset->begin();
350  *pCol = static_cast<SCCOL>(nPos);
351  if ( bFound )
352  return true;
353  sal_uInt16 nCount = pOffset->size();
354  if ( !nCount )
355  return false;
356  // nPos is the position of insertion, that's where the next higher one is (or isn't)
357  if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
358  return true;
359  // Not smaller than everything else? Then compare with the next lower one
360  else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
361  {
362  (*pCol)--;
363  return true;
364  }
365  return false;
366 }
367 
368 void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
369  sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
370 {
371  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
372  SCCOL nPos;
373  if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
374  nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
375  else
376  pOffset->insert( nOffset );
377  if ( nWidth )
378  {
379  if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
380  nWidth = static_cast<sal_uInt16>((*pOffset)[nPos]) - nOffset;
381  else
382  pOffset->insert( nOffset + nWidth );
383  }
384 }
385 
386 void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
387  sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
388 {
389  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
390  SCCOL nPos;
391  if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
392  nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
393  else
394  pOffset->insert( nOffset );
395  if ( nWidth )
396  {
397  if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
398  pOffset->insert( nOffset + nWidth );
399  }
400 }
401 
402 void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
403  sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
404 {
405  OSL_ENSURE( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
406  SCCOL nPos;
407  if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
408  {
409  if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
410  nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
411  else
412  pOffset->insert( nNewOffset );
413  return ;
414  }
415  nOldOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
416  SCCOL nPos2;
417  if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
418  {
419  nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos2]);
420  return ;
421  }
422  tools::Long nDiff = nNewOffset - nOldOffset;
423  if ( nDiff < 0 )
424  {
425  do
426  {
427  const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
428  } while ( nPos-- );
429  }
430  else
431  {
432  do
433  {
434  const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
435  } while ( ++nPos < static_cast<sal_uInt16>(pOffset->size()) );
436  }
437 }
438 
440 {
441  if ( !mpDoc->ValidCol(pE->nCol) )
442  return;
443 
444 // Or else this would create a wrong value at ScAddress (chance for an infinite loop)!
445  bool bBadCol = false;
446  bool bAgain;
447  ScRange aRange( pE->nCol, pE->nRow, 0,
448  pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
449  do
450  {
451  bAgain = false;
452  for ( size_t i = 0, nRanges = xLockedList->size(); i < nRanges; ++i )
453  {
454  ScRange & rR = (*xLockedList)[i];
455  if ( rR.Intersects( aRange ) )
456  {
457  pE->nCol = rR.aEnd.Col() + 1;
458  SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
459  if ( pE->nCol > mpDoc->MaxCol() || nTmp > mpDoc->MaxCol() )
460  bBadCol = true;
461  else
462  {
463  bAgain = true;
464  aRange.aStart.SetCol( pE->nCol );
465  aRange.aEnd.SetCol( nTmp );
466  }
467  break;
468  }
469  }
470  } while ( bAgain );
471  if ( bJoin && !bBadCol )
472  xLockedList->Join( aRange );
473 }
474 
476 {
478 
479  std::stack< std::unique_ptr<ScHTMLAdjustStackEntry> > aStack;
480  sal_uInt16 nTab = 0;
481  SCCOL nLastCol = SCCOL_MAX;
482  SCROW nNextRow = 0;
483  SCROW nCurRow = 0;
484  sal_uInt16 nPageWidth = static_cast<sal_uInt16>(aPageSize.Width());
485  InnerMap* pTab = nullptr;
486  for (auto& pE : maList)
487  {
488  if ( pE->nTab < nTab )
489  { // Table finished
490  if ( !aStack.empty() )
491  {
492  std::unique_ptr<ScHTMLAdjustStackEntry> pS = std::move(aStack.top());
493  aStack.pop();
494 
495  nLastCol = pS->nLastCol;
496  nNextRow = pS->nNextRow;
497  nCurRow = pS->nCurRow;
498  }
499  nTab = pE->nTab;
500  if (pTables)
501  {
502  OuterMap::const_iterator it = pTables->find( nTab );
503  if ( it != pTables->end() )
504  pTab = it->second;
505  }
506 
507  }
508  SCROW nRow = pE->nRow;
509  if ( pE->nCol <= nLastCol )
510  { // Next row
511  if ( pE->nRow < nNextRow )
512  pE->nRow = nCurRow = nNextRow;
513  else
514  nCurRow = nNextRow = pE->nRow;
515  SCROW nR = 0;
516  if ( pTab )
517  {
518  InnerMap::const_iterator it = pTab->find( nCurRow );
519  if ( it != pTab->end() )
520  nR = it->second;
521  }
522  if ( nR )
523  nNextRow += nR;
524  else
525  nNextRow++;
526  }
527  else
528  pE->nRow = nCurRow;
529  nLastCol = pE->nCol; // Read column
530  if ( pE->nTab > nTab )
531  { // New table
532  aStack.push( std::make_unique<ScHTMLAdjustStackEntry>(
533  nLastCol, nNextRow, nCurRow ) );
534  nTab = pE->nTab;
535  if ( pTables )
536  {
537  OuterMap::const_iterator it = pTables->find( nTab );
538  if ( it != pTables->end() )
539  pTab = it->second;
540  }
541  // New line spacing
542  SCROW nR = 0;
543  if ( pTab )
544  {
545  InnerMap::const_iterator it = pTab->find( nCurRow );
546  if ( it != pTab->end() )
547  nR = it->second;
548  }
549  if ( nR )
550  nNextRow = nCurRow + nR;
551  else
552  nNextRow = nCurRow + 1;
553  }
554  if ( nTab == 0 )
555  pE->nWidth = nPageWidth;
556  else
557  { // Real table, no paragraphs on the field
558  if ( pTab )
559  {
560  SCROW nRowSpan = pE->nRowOverlap;
561  for ( SCROW j=0; j < nRowSpan; j++ )
562  { // RowSpan resulting from merged rows
563  SCROW nRows = 0;
564  InnerMap::const_iterator it = pTab->find( nRow+j );
565  if ( it != pTab->end() )
566  nRows = it->second;
567  if ( nRows > 1 )
568  {
569  pE->nRowOverlap += nRows - 1;
570  if ( j == 0 )
571  { // Merged rows move the next row
572  SCROW nTmp = nCurRow + nRows;
573  if ( nNextRow < nTmp )
574  nNextRow = nTmp;
575  }
576  }
577  }
578  }
579  }
580  // Real column
581  (void)SeekOffset( &maColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
582  SCCOL nColBeforeSkip = pE->nCol;
583  SkipLocked(pE.get(), false);
584  if ( pE->nCol != nColBeforeSkip )
585  {
586  SCCOL nCount = static_cast<SCCOL>(maColOffset.size());
587  if ( nCount <= pE->nCol )
588  {
589  pE->nOffset = static_cast<sal_uInt16>(maColOffset[nCount-1]);
590  MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
591  }
592  else
593  {
594  pE->nOffset = static_cast<sal_uInt16>(maColOffset[pE->nCol]);
595  }
596  }
597  SCCOL nPos;
598  if ( pE->nWidth && SeekOffset( &maColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
599  pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
600  else
601  {
602  //FIXME: This may not be correct, but works anyway ...
603  pE->nColOverlap = 1;
604  }
605  xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
606  pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
607  // Take over MaxDimensions
608  SCCOL nColTmp = pE->nCol + pE->nColOverlap;
609  if ( nColMax < nColTmp )
610  nColMax = nColTmp;
611  SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
612  if ( nRowMax < nRowTmp )
613  nRowMax = nRowTmp;
614  }
615 }
616 
618 {
619  if ( pE->nWidth )
620  return pE->nWidth;
621  sal_Int32 nTmp = std::min( static_cast<sal_Int32>( pE->nCol -
622  nColCntStart + pE->nColOverlap),
623  static_cast<sal_Int32>( pLocalColOffset->size() - 1));
624  SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
625  sal_uInt16 nOff2 = static_cast<sal_uInt16>((*pLocalColOffset)[nPos]);
626  if ( pE->nOffset < nOff2 )
627  return nOff2 - pE->nOffset;
628  return 0;
629 }
630 
632 {
633  SCCOL nCol;
634  if ( !nTableWidth )
635  nTableWidth = static_cast<sal_uInt16>(aPageSize.Width());
636  SCCOL nColsPerRow = nMaxCol - nColCntStart;
637  if ( nColsPerRow <= 0 )
638  nColsPerRow = 1;
639  if ( pLocalColOffset->size() <= 2 )
640  { // Only PageSize, there was no width setting
641  sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
642  sal_uInt16 nOff = nColOffsetStart;
644  for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
645  {
646  MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
647  }
648  nTableWidth = static_cast<sal_uInt16>(pLocalColOffset->back() - pLocalColOffset->front());
649  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
650  {
651  auto& pE = maList[ i ];
652  if ( pE->nTab == nTable )
653  {
654  pE->nOffset = static_cast<sal_uInt16>((*pLocalColOffset)[pE->nCol - nColCntStart]);
655  pE->nWidth = 0; // to be recalculated later
656  }
657  }
658  }
659  else
660  { // Some without width
661  // Why actually no pE?
662  if ( nFirstTableCell < maList.size() )
663  {
664  std::unique_ptr<sal_uInt16[]> pOffsets(new sal_uInt16[ nColsPerRow+1 ]);
665  memset( pOffsets.get(), 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
666  std::unique_ptr<sal_uInt16[]> pWidths(new sal_uInt16[ nColsPerRow ]);
667  memset( pWidths.get(), 0, nColsPerRow * sizeof(sal_uInt16) );
668  pOffsets[0] = nColOffsetStart;
669  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
670  {
671  auto& pE = maList[ i ];
672  if ( pE->nTab == nTable && pE->nWidth )
673  {
674  nCol = pE->nCol - nColCntStart;
675  if ( nCol < nColsPerRow )
676  {
677  if ( pE->nColOverlap == 1 )
678  {
679  if ( pWidths[nCol] < pE->nWidth )
680  pWidths[nCol] = pE->nWidth;
681  }
682  else
683  { // try to find a single undefined width
684  sal_uInt16 nTotal = 0;
685  bool bFound = false;
686  SCCOL nHere = 0;
687  SCCOL nStop = std::min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
688  for ( ; nCol < nStop; nCol++ )
689  {
690  if ( pWidths[nCol] )
691  nTotal = nTotal + pWidths[nCol];
692  else
693  {
694  if ( bFound )
695  {
696  bFound = false;
697  break; // for
698  }
699  bFound = true;
700  nHere = nCol;
701  }
702  }
703  if ( bFound && pE->nWidth > nTotal )
704  pWidths[nHere] = pE->nWidth - nTotal;
705  }
706  }
707  }
708  }
709  sal_uInt16 nWidths = 0;
710  sal_uInt16 nUnknown = 0;
711  for ( nCol = 0; nCol < nColsPerRow; nCol++ )
712  {
713  if ( pWidths[nCol] )
714  nWidths = nWidths + pWidths[nCol];
715  else
716  nUnknown++;
717  }
718  if ( nUnknown )
719  {
720  sal_uInt16 nW = ((nWidths < nTableWidth) ?
721  ((nTableWidth - nWidths) / nUnknown) :
722  (nTableWidth / nUnknown));
723  for ( nCol = 0; nCol < nColsPerRow; nCol++ )
724  {
725  if ( !pWidths[nCol] )
726  pWidths[nCol] = nW;
727  }
728  }
729  for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
730  {
731  pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
732  }
734  for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
735  {
736  MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
737  }
738  nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
739 
740  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
741  {
742  auto& pE = maList[ i ];
743  if ( pE->nTab == nTable )
744  {
745  nCol = pE->nCol - nColCntStart;
746  OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
747  if ( nCol < nColsPerRow )
748  {
749  pE->nOffset = pOffsets[nCol];
750  nCol = nCol + pE->nColOverlap;
751  if ( nCol > nColsPerRow )
752  nCol = nColsPerRow;
753  pE->nWidth = pOffsets[nCol] - pE->nOffset;
754  }
755  }
756  }
757  }
758  }
759  if ( !pLocalColOffset->empty() )
760  {
761  sal_uInt16 nMax = static_cast<sal_uInt16>(pLocalColOffset->back());
762  if ( aPageSize.Width() < nMax )
763  aPageSize.setWidth( nMax );
764  }
765  for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
766  {
767  auto& pE = maList[ i ];
768  if ( pE->nTab == nTable )
769  {
770  if ( !pE->nWidth )
771  {
772  pE->nWidth = GetWidth(pE.get());
773  OSL_ENSURE( pE->nWidth, "SetWidths: pE->nWidth == 0" );
774  }
775  MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
776  }
777  }
778 }
779 
781 {
782  if ( pE->nCol == SCCOL_MAX )
783  pE->nCol = nColCnt;
784  if ( pE->nRow == SCROW_MAX )
785  pE->nRow = nRowCnt;
786  SCCOL nCol = pE->nCol;
787  SkipLocked( pE ); // Change of columns to the right
788 
789  if ( nCol < pE->nCol )
790  { // Replaced
791  nCol = pE->nCol - nColCntStart;
792  SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size());
793  if ( nCol < nCount )
794  nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCol]);
795  else
796  nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCount - 1]);
797  }
798  pE->nOffset = nColOffset;
799  sal_uInt16 nWidth = GetWidth( pE );
801  if ( pE->nWidth )
802  pE->nWidth = nWidth;
803  nColOffset = pE->nOffset + nWidth;
806 }
807 
809 {
810  bInCell = false;
811  if ( bTabInTabCell )
812  { // From the stack in TableOff
813  bTabInTabCell = false;
814  NewActEntry(maList.back().get()); // New free flying mxActEntry
815  return ;
816  }
817  if (mxActEntry->nTab == 0)
818  mxActEntry->nWidth = static_cast<sal_uInt16>(aPageSize.Width());
819  Colonize(mxActEntry.get());
820  nColCnt = mxActEntry->nCol + mxActEntry->nColOverlap;
821  if ( nMaxCol < nColCnt )
822  nMaxCol = nColCnt; // TableStack MaxCol
823  if ( nColMax < nColCnt )
824  nColMax = nColCnt; // Global MaxCol for ScEEParser GetDimensions!
825  EntryEnd(mxActEntry.get(), pInfo->aSelection);
826  ESelection& rSel = mxActEntry->aSel;
827  while ( rSel.nStartPara < rSel.nEndPara
828  && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
829  { // Strip preceding empty paragraphs
830  rSel.nStartPara++;
831  }
832  while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
833  { // Strip successive empty paragraphs
834  rSel.nEndPara--;
835  rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
836  }
837  if ( rSel.nStartPara > rSel.nEndPara )
838  { // Gives GPF in CreateTextObject
839  OSL_FAIL( "CloseEntry: EditEngine ESelection Start > End" );
840  rSel.nEndPara = rSel.nStartPara;
841  }
842  if ( rSel.HasRange() )
843  mxActEntry->aItemSet.Put( ScLineBreakCell(true) );
844  maList.push_back(mxActEntry);
845  NewActEntry(mxActEntry.get()); // New free flying mxActEntry
846 }
847 
848 IMPL_LINK( ScHTMLLayoutParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
849 {
850  switch ( rInfo.eState )
851  {
852  case HtmlImportState::NextToken:
853  ProcToken( &rInfo );
854  break;
855  case HtmlImportState::Start:
856  break;
857  case HtmlImportState::End:
858  if ( rInfo.aSelection.nEndPos )
859  {
860  // If text remains: create paragraph, without calling CloseEntry().
861  if( bInCell ) // ...but only in opened table cells.
862  {
863  bInCell = false;
864  NextRow( &rInfo );
865  bInCell = true;
866  }
867  CloseEntry( &rInfo );
868  }
869  while ( nTableLevel > 0 )
870  TableOff( &rInfo ); // close tables, if </TABLE> missing
871  break;
872  case HtmlImportState::SetAttr:
873  break;
874  case HtmlImportState::InsertText:
875  break;
876  case HtmlImportState::InsertPara:
877  if ( nTableLevel < 1 )
878  {
879  CloseEntry( &rInfo );
880  NextRow( &rInfo );
881  }
882  break;
883  case HtmlImportState::InsertField:
884  break;
885  default:
886  OSL_FAIL("HTMLImportHdl: unknown ImportInfo.eState");
887  }
888 }
889 
891 {
892  if ( bInCell )
893  CloseEntry( pInfo );
894  if ( !nTableLevel )
895  {
896  OSL_FAIL( "dumbo doc! <TH> or <TD> without previous <TABLE>" );
897  TableOn( pInfo );
898  }
899  bInCell = true;
900  bool bHorJustifyCenterTH = (pInfo->nToken == HtmlTokenId::TABLEHEADER_ON);
901  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
902  for (const auto & rOption : rOptions)
903  {
904  switch( rOption.GetToken() )
905  {
906  case HtmlOptionId::COLSPAN:
907  {
908  mxActEntry->nColOverlap = static_cast<SCCOL>(rOption.GetString().toInt32());
909  }
910  break;
911  case HtmlOptionId::ROWSPAN:
912  {
913  mxActEntry->nRowOverlap = static_cast<SCROW>(rOption.GetString().toInt32());
914  }
915  break;
916  case HtmlOptionId::ALIGN:
917  {
918  bHorJustifyCenterTH = false;
919  SvxCellHorJustify eVal;
920  const OUString& rOptVal = rOption.GetString();
921  if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
922  eVal = SvxCellHorJustify::Right;
923  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
924  eVal = SvxCellHorJustify::Center;
925  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
926  eVal = SvxCellHorJustify::Left;
927  else
928  eVal = SvxCellHorJustify::Standard;
929  if ( eVal != SvxCellHorJustify::Standard )
930  mxActEntry->aItemSet.Put(SvxHorJustifyItem(eVal, ATTR_HOR_JUSTIFY));
931  }
932  break;
933  case HtmlOptionId::VALIGN:
934  {
935  SvxCellVerJustify eVal;
936  const OUString& rOptVal = rOption.GetString();
937  if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
938  eVal = SvxCellVerJustify::Top;
939  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
940  eVal = SvxCellVerJustify::Center;
941  else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
942  eVal = SvxCellVerJustify::Bottom;
943  else
944  eVal = SvxCellVerJustify::Standard;
945  mxActEntry->aItemSet.Put(SvxVerJustifyItem(eVal, ATTR_VER_JUSTIFY));
946  }
947  break;
948  case HtmlOptionId::WIDTH:
949  {
950  mxActEntry->nWidth = GetWidthPixel(rOption);
951  }
952  break;
953  case HtmlOptionId::BGCOLOR:
954  {
955  Color aColor;
956  rOption.GetColor( aColor );
957  mxActEntry->aItemSet.Put(SvxBrushItem(aColor, ATTR_BACKGROUND));
958  }
959  break;
960  case HtmlOptionId::SDVAL:
961  {
962  mxActEntry->pValStr = rOption.GetString();
963  }
964  break;
965  case HtmlOptionId::SDNUM:
966  {
967  mxActEntry->pNumStr = rOption.GetString();
968  }
969  break;
970  default: break;
971  }
972  }
973 
974  mxActEntry->nCol = nColCnt;
975  mxActEntry->nRow = nRowCnt;
976  mxActEntry->nTab = nTable;
977 
978  if ( bHorJustifyCenterTH )
979  mxActEntry->aItemSet.Put(
980  SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY) );
981 }
982 
984 {
985  if ( nColCnt > nColCntStart )
986  NextRow( pInfo ); // The optional TableRowOff wasn't there
988 }
989 
991 {
992  NextRow( pInfo );
993 }
994 
996 {
997  if ( bInCell )
998  CloseEntry( pInfo ); // Only if it really was one
999 }
1000 
1002 {
1003  if ( ++nTableLevel > 1 )
1004  { // Table in Table
1005  sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize()
1006  Colonize(mxActEntry.get());
1007  aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
1011  bFirstRow ) );
1012  sal_uInt16 nLastWidth = nTableWidth;
1013  nTableWidth = GetWidth(mxActEntry.get());
1014  if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
1015  { // There must be more than one, so this one cannot be enough
1016  nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
1017  }
1018  nLastWidth = nTableWidth;
1019  if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
1020  { // It can still be TD or TH, if we didn't have a TABLE earlier
1021  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1022  for (const auto & rOption : rOptions)
1023  {
1024  switch( rOption.GetToken() )
1025  {
1026  case HtmlOptionId::WIDTH:
1027  { // Percent: of document width or outer cell
1028  nTableWidth = GetWidthPixel( rOption );
1029  }
1030  break;
1031  case HtmlOptionId::BORDER:
1032  // Border is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1033  break;
1034  default: break;
1035  }
1036  }
1037  }
1038  bInCell = false;
1039  if ( bTabInTabCell && (nTableWidth >= nLastWidth) )
1040  { // Multiple tables in one cell, underneath each other
1041  bTabInTabCell = false;
1042  NextRow( pInfo );
1043  }
1044  else
1045  { // It start's in this cell or next to each other
1046  bTabInTabCell = false;
1048  nColOffset = nTmpColOffset;
1050  }
1051 
1052  NewActEntry(!maList.empty() ? maList.back().get() : nullptr); // New free flying mxActEntry
1053  xLockedList = new ScRangeList;
1054  }
1055  else
1056  { // Simple table at the document level
1057  EntryEnd(mxActEntry.get(), pInfo->aSelection);
1058  if (mxActEntry->aSel.HasRange())
1059  { // Flying text left
1060  CloseEntry( pInfo );
1061  NextRow( pInfo );
1062  }
1063  aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
1067  bFirstRow ) );
1068  // As soon as we have multiple tables we need to be tolerant with the offsets.
1069  if (nMaxTable > 0)
1071  nTableWidth = 0;
1072  if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
1073  {
1074  // It can still be TD or TH, if we didn't have a TABLE earlier
1075  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1076  for (const auto & rOption : rOptions)
1077  {
1078  switch( rOption.GetToken() )
1079  {
1080  case HtmlOptionId::WIDTH:
1081  { // Percent: of document width or outer cell
1082  nTableWidth = GetWidthPixel( rOption );
1083  }
1084  break;
1085  case HtmlOptionId::BORDER:
1086  //BorderOn is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1087  break;
1088  default: break;
1089  }
1090  }
1091  }
1092  }
1093  nTable = ++nMaxTable;
1094  bFirstRow = true;
1095  nFirstTableCell = maList.size();
1096 
1099 }
1100 
1102 {
1103  if ( bInCell )
1104  CloseEntry( pInfo );
1105  if ( nColCnt > nColCntStart )
1106  TableRowOff( pInfo ); // The optional TableRowOff wasn't
1107  if ( !nTableLevel )
1108  {
1109  OSL_FAIL( "dumbo doc! </TABLE> without opening <TABLE>" );
1110  return ;
1111  }
1112  if ( --nTableLevel > 0 )
1113  { // Table in Table done
1114  if ( !aTableStack.empty() )
1115  {
1116  std::unique_ptr<ScHTMLTableStackEntry> pS = std::move(aTableStack.top());
1117  aTableStack.pop();
1118 
1119  auto& pE = pS->xCellEntry;
1120  SCROW nRows = nRowCnt - pS->nRowCnt;
1121  if ( nRows > 1 )
1122  { // Insert size of table at this position
1123  SCROW nRow = pS->nRowCnt;
1124  sal_uInt16 nTab = pS->nTable;
1125  if ( !pTables )
1126  pTables.reset( new OuterMap );
1127  // Height of outer table
1128  OuterMap::const_iterator it = pTables->find( nTab );
1129  InnerMap* pTab1;
1130  if ( it == pTables->end() )
1131  {
1132  pTab1 = new InnerMap;
1133  (*pTables)[ nTab ] = pTab1;
1134  }
1135  else
1136  pTab1 = it->second;
1137  SCROW nRowSpan = pE->nRowOverlap;
1138  SCROW nRowKGV;
1139  SCROW nRowsPerRow1; // Outer table
1140  SCROW nRowsPerRow2; // Inner table
1141  if ( nRowSpan > 1 )
1142  { // LCM to which we can map the inner and outer rows
1143  nRowKGV = std::lcm( nRowSpan, nRows );
1144  nRowsPerRow1 = nRowKGV / nRowSpan;
1145  nRowsPerRow2 = nRowKGV / nRows;
1146  }
1147  else
1148  {
1149  nRowKGV = nRowsPerRow1 = nRows;
1150  nRowsPerRow2 = 1;
1151  }
1152  InnerMap* pTab2 = nullptr;
1153  if ( nRowsPerRow2 > 1 )
1154  { // Height of the inner table
1155  pTab2 = new InnerMap;
1156  (*pTables)[ nTable ] = pTab2;
1157  }
1158  // Abuse void* Data entry of the Table class for height mapping
1159  if ( nRowKGV > 1 )
1160  {
1161  if ( nRowsPerRow1 > 1 )
1162  { // Outer
1163  for ( SCROW j=0; j < nRowSpan; j++ )
1164  {
1165  sal_uLong nRowKey = nRow + j;
1166  SCROW nR = (*pTab1)[ nRowKey ];
1167  if ( !nR )
1168  (*pTab1)[ nRowKey ] = nRowsPerRow1;
1169  else if ( nRowsPerRow1 > nR )
1170  (*pTab1)[ nRowKey ] = nRowsPerRow1;
1171  //TODO: How can we improve on this?
1172  else if ( nRowsPerRow1 < nR && nRowSpan == 1
1173  && nTable == nMaxTable )
1174  { // Still some space left, merge in a better way (if possible)
1175  SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
1176  nR += nAdd;
1177  if ( (nR % nRows) == 0 )
1178  { // Only if representable
1179  SCROW nR2 = (*pTab1)[ nRowKey+1 ];
1180  if ( nR2 > nAdd )
1181  { // Only if we really have enough space
1182  (*pTab1)[ nRowKey ] = nR;
1183  (*pTab1)[ nRowKey+1 ] = nR2 - nAdd;
1184  nRowsPerRow2 = nR / nRows;
1185  }
1186  }
1187  }
1188  }
1189  }
1190  if ( nRowsPerRow2 > 1 )
1191  { // Inner
1192  if ( !pTab2 )
1193  { // nRowsPerRow2 could be've been incremented
1194  pTab2 = new InnerMap;
1195  (*pTables)[ nTable ] = pTab2;
1196  }
1197  for ( SCROW j=0; j < nRows; j++ )
1198  {
1199  sal_uLong nRowKey = nRow + j;
1200  (*pTab2)[ nRowKey ] = nRowsPerRow2;
1201  }
1202  }
1203  }
1204  }
1205 
1206  SetWidths();
1207 
1208  if ( !pE->nWidth )
1209  pE->nWidth = nTableWidth;
1210  else if ( pE->nWidth < nTableWidth )
1211  {
1212  sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
1213  sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
1214  ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
1215  sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
1216  pE->nWidth = nNewOffset - pE->nOffset;
1217  pS->nTableWidth = pS->nTableWidth + nTmp;
1218  if ( pS->nColOffset >= nOldOffset )
1219  pS->nColOffset = pS->nColOffset + nTmp;
1220  }
1221 
1222  nColCnt = pE->nCol + pE->nColOverlap;
1223  nRowCnt = pS->nRowCnt;
1224  nColCntStart = pS->nColCntStart;
1225  nMaxCol = pS->nMaxCol;
1226  nTable = pS->nTable;
1227  nTableWidth = pS->nTableWidth;
1228  nFirstTableCell = pS->nFirstTableCell;
1229  nColOffset = pS->nColOffset;
1230  nColOffsetStart = pS->nColOffsetStart;
1231  bFirstRow = pS->bFirstRow;
1232  xLockedList = pS->xLockedList;
1233  pLocalColOffset = pS->pLocalColOffset;
1234  // mxActEntry is kept around if a table is started in the same row
1235  // (anything's possible in HTML); will be deleted by CloseEntry
1236  mxActEntry = pE;
1237  }
1238  bTabInTabCell = true;
1239  bInCell = true;
1240  }
1241  else
1242  { // Simple table finished
1243  SetWidths();
1244  nMaxCol = 0;
1245  nTable = 0;
1246  if ( !aTableStack.empty() )
1247  {
1248  ScHTMLTableStackEntry* pS = aTableStack.top().get();
1249  delete pLocalColOffset;
1251  aTableStack.pop();
1252  }
1253  }
1254 }
1255 
1257 {
1258  mxActEntry->maImageList.push_back(std::make_unique<ScHTMLImage>());
1259  ScHTMLImage* pImage = mxActEntry->maImageList.back().get();
1260  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1261  for (const auto & rOption : rOptions)
1262  {
1263  switch( rOption.GetToken() )
1264  {
1265  case HtmlOptionId::SRC:
1266  {
1267  pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, rOption.GetString() );
1268  }
1269  break;
1270  case HtmlOptionId::ALT:
1271  {
1272  if (!mxActEntry->bHasGraphic)
1273  { // ALT text only if not any image loaded
1274  if (!mxActEntry->aAltText.isEmpty())
1275  mxActEntry->aAltText += "; ";
1276 
1277  mxActEntry->aAltText += rOption.GetString();
1278  }
1279  }
1280  break;
1281  case HtmlOptionId::WIDTH:
1282  {
1283  pImage->aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
1284  }
1285  break;
1286  case HtmlOptionId::HEIGHT:
1287  {
1288  pImage->aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
1289  }
1290  break;
1291  case HtmlOptionId::HSPACE:
1292  {
1293  pImage->aSpace.setX( static_cast<tools::Long>(rOption.GetNumber()) );
1294  }
1295  break;
1296  case HtmlOptionId::VSPACE:
1297  {
1298  pImage->aSpace.setY( static_cast<tools::Long>(rOption.GetNumber()) );
1299  }
1300  break;
1301  default: break;
1302  }
1303  }
1304  if (pImage->aURL.isEmpty())
1305  {
1306  OSL_FAIL( "Image: graphic without URL ?!?" );
1307  return ;
1308  }
1309 
1310  sal_uInt16 nFormat;
1311  std::unique_ptr<Graphic> pGraphic(new Graphic);
1313  if ( ERRCODE_NONE != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
1314  *pGraphic, &rFilter, &nFormat ) )
1315  {
1316  return ; // Bad luck
1317  }
1318  if (!mxActEntry->bHasGraphic)
1319  { // discard any ALT text in this cell if we have any image
1320  mxActEntry->bHasGraphic = true;
1321  mxActEntry->aAltText.clear();
1322  }
1323  pImage->aFilterName = rFilter.GetImportFormatName( nFormat );
1324  pImage->pGraphic = std::move( pGraphic );
1325  if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
1326  {
1328  pImage->aSize = pDefaultDev->LogicToPixel( pImage->pGraphic->GetPrefSize(),
1329  pImage->pGraphic->GetPrefMapMode() );
1330  }
1331  if (mxActEntry->maImageList.empty())
1332  return;
1333 
1334  tools::Long nWidth = 0;
1335  for (const std::unique_ptr<ScHTMLImage> & pI : mxActEntry->maImageList)
1336  {
1337  if ( pI->nDir & nHorizontal )
1338  nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
1339  else
1340  nWidth = 0;
1341  }
1342  if ( mxActEntry->nWidth
1343  && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
1344  >= mxActEntry->nWidth) )
1345  mxActEntry->maImageList.back()->nDir = nVertical;
1346 }
1347 
1349 {
1350  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1351  for (const auto & rOption : rOptions)
1352  {
1353  if( rOption.GetToken() == HtmlOptionId::WIDTH )
1354  {
1355  sal_uInt16 nVal = GetWidthPixel( rOption );
1356  MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
1357  nColOffset = nColOffset + nVal;
1358  }
1359  }
1360 }
1361 
1362 sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption& rOption )
1363 {
1364  const OUString& rOptVal = rOption.GetString();
1365  if ( rOptVal.indexOf('%') != -1 )
1366  { // Percent
1367  sal_uInt16 nW = (nTableWidth ? nTableWidth : static_cast<sal_uInt16>(aPageSize.Width()));
1368  return static_cast<sal_uInt16>((rOption.GetNumber() * nW) / 100);
1369  }
1370  else
1371  {
1372  if ( rOptVal.indexOf('*') != -1 )
1373  { // Relative to what?
1374  // TODO: Collect all relative values in ColArray and then MakeCol
1375  return 0;
1376  }
1377  else
1378  return static_cast<sal_uInt16>(rOption.GetNumber()); // Pixel
1379  }
1380 }
1381 
1383 {
1384  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1385  for (const auto & rOption : rOptions)
1386  {
1387  if( rOption.GetToken() == HtmlOptionId::NAME )
1388  mxActEntry->pName = rOption.GetString();
1389  }
1390 }
1391 
1393 {
1394  ESelection& rSel = mxActEntry->aSel;
1395  return rSel.nStartPara == rSel.nEndPara &&
1396  rSel.nStartPara <= pInfo->aSelection.nEndPara &&
1397  pEdit->GetTextLen( rSel.nStartPara ) == 0;
1398 }
1399 
1401 {
1402  if ( !IsAtBeginningOfText( pInfo ) )
1403  return;
1404 
1405 // Only at the start of the text; applies to whole line
1406  const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1407  for (const auto & rOption : rOptions)
1408  {
1409  switch( rOption.GetToken() )
1410  {
1411  case HtmlOptionId::FACE :
1412  {
1413  const OUString& rFace = rOption.GetString();
1414  OUStringBuffer aFontName;
1415  sal_Int32 nPos = 0;
1416  while( nPos != -1 )
1417  {
1418  // Font list, VCL uses the semicolon as separator
1419  // HTML uses the comma
1420  OUString aFName = rFace.getToken( 0, ',', nPos );
1421  aFName = comphelper::string::strip(aFName, ' ');
1422  if( !aFontName.isEmpty() )
1423  aFontName.append(";");
1424  aFontName.append(aFName);
1425  }
1426  if ( !aFontName.isEmpty() )
1427  mxActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
1428  aFontName.makeStringAndClear(), EMPTY_OUSTRING, PITCH_DONTKNOW,
1429  RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
1430  }
1431  break;
1432  case HtmlOptionId::SIZE :
1433  {
1434  sal_uInt16 nSize = static_cast<sal_uInt16>(rOption.GetNumber());
1435  if ( nSize == 0 )
1436  nSize = 1;
1437  else if ( nSize > SC_HTML_FONTSIZES )
1438  nSize = SC_HTML_FONTSIZES;
1439  mxActEntry->aItemSet.Put( SvxFontHeightItem(
1440  maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
1441  }
1442  break;
1443  case HtmlOptionId::COLOR :
1444  {
1445  Color aColor;
1446  rOption.GetColor( aColor );
1447  mxActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
1448  }
1449  break;
1450  default: break;
1451  }
1452  }
1453 }
1454 
1456 {
1457  switch ( pInfo->nToken )
1458  {
1459  case HtmlTokenId::META:
1460  {
1461  HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser);
1462  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1463  mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
1464  pParser->ParseMetaOptions(
1465  xDPS->getDocumentProperties(),
1467  }
1468  break;
1469  case HtmlTokenId::TITLE_ON:
1470  {
1471  bInTitle = true;
1472  aString.clear();
1473  }
1474  break;
1475  case HtmlTokenId::TITLE_OFF:
1476  {
1477  if ( bInTitle && !aString.isEmpty() )
1478  {
1479  // Remove blanks from line breaks
1480  aString = aString.trim();
1481  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1483  uno::UNO_QUERY_THROW);
1484  xDPS->getDocumentProperties()->setTitle(aString);
1485  }
1486  bInTitle = false;
1487  }
1488  break;
1489  case HtmlTokenId::TABLE_ON:
1490  {
1491  TableOn( pInfo );
1492  }
1493  break;
1494  case HtmlTokenId::COL_ON:
1495  {
1496  ColOn( pInfo );
1497  }
1498  break;
1499  case HtmlTokenId::TABLEHEADER_ON: // Opens row
1500  {
1501  if ( bInCell )
1502  CloseEntry( pInfo );
1503  // Do not set bInCell to true, TableDataOn does that
1504  mxActEntry->aItemSet.Put(
1506  [[fallthrough]];
1507  }
1508  case HtmlTokenId::TABLEDATA_ON: // Opens cell
1509  {
1510  TableDataOn( pInfo );
1511  }
1512  break;
1513  case HtmlTokenId::TABLEHEADER_OFF:
1514  case HtmlTokenId::TABLEDATA_OFF: // Closes cell
1515  {
1516  TableDataOff( pInfo );
1517  }
1518  break;
1519  case HtmlTokenId::TABLEROW_ON: // Before first cell in row
1520  {
1521  TableRowOn( pInfo );
1522  }
1523  break;
1524  case HtmlTokenId::TABLEROW_OFF: // After last cell in row
1525  {
1526  TableRowOff( pInfo );
1527  }
1528  break;
1529  case HtmlTokenId::TABLE_OFF:
1530  {
1531  TableOff( pInfo );
1532  }
1533  break;
1534  case HtmlTokenId::IMAGE:
1535  {
1536  Image( pInfo );
1537  }
1538  break;
1539  case HtmlTokenId::PARABREAK_OFF:
1540  { // We continue vertically after an image
1541  if (!mxActEntry->maImageList.empty())
1542  mxActEntry->maImageList.back()->nDir = nVertical;
1543  }
1544  break;
1545  case HtmlTokenId::ANCHOR_ON:
1546  {
1547  AnchorOn( pInfo );
1548  }
1549  break;
1550  case HtmlTokenId::FONT_ON :
1551  {
1552  FontOn( pInfo );
1553  }
1554  break;
1555  case HtmlTokenId::BIGPRINT_ON :
1556  {
1557  // TODO: Remember current font size and increase by 1
1558  if ( IsAtBeginningOfText( pInfo ) )
1559  mxActEntry->aItemSet.Put( SvxFontHeightItem(
1560  maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
1561  }
1562  break;
1563  case HtmlTokenId::SMALLPRINT_ON :
1564  {
1565  // TODO: Remember current font size and decrease by 1
1566  if ( IsAtBeginningOfText( pInfo ) )
1567  mxActEntry->aItemSet.Put( SvxFontHeightItem(
1568  maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
1569  }
1570  break;
1571  case HtmlTokenId::BOLD_ON :
1572  case HtmlTokenId::STRONG_ON :
1573  {
1574  if ( IsAtBeginningOfText( pInfo ) )
1575  mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1576  ATTR_FONT_WEIGHT ) );
1577  }
1578  break;
1579  case HtmlTokenId::ITALIC_ON :
1580  case HtmlTokenId::EMPHASIS_ON :
1581  case HtmlTokenId::ADDRESS_ON :
1582  case HtmlTokenId::BLOCKQUOTE_ON :
1583  case HtmlTokenId::BLOCKQUOTE30_ON :
1584  case HtmlTokenId::CITATION_ON :
1585  case HtmlTokenId::VARIABLE_ON :
1586  {
1587  if ( IsAtBeginningOfText( pInfo ) )
1588  mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1589  ATTR_FONT_POSTURE ) );
1590  }
1591  break;
1592  case HtmlTokenId::DEFINSTANCE_ON :
1593  {
1594  if ( IsAtBeginningOfText( pInfo ) )
1595  {
1596  mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1597  ATTR_FONT_WEIGHT ) );
1598  mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1599  ATTR_FONT_POSTURE ) );
1600  }
1601  }
1602  break;
1603  case HtmlTokenId::UNDERLINE_ON :
1604  {
1605  if ( IsAtBeginningOfText( pInfo ) )
1607  ATTR_FONT_UNDERLINE ) );
1608  }
1609  break;
1610  case HtmlTokenId::TEXTTOKEN:
1611  {
1612  if ( bInTitle )
1613  aString += pInfo->aText;
1614  }
1615  break;
1616  default: ;
1617  }
1618 }
1619 
1620 // HTML DATA QUERY PARSER
1621 
1622 template< typename Type >
1623 static Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
1624 { return std::clamp( rValue, rMin, rMax ); }
1625 
1626 ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
1627  ScEEParseEntry( rItemSet ),
1628  mbImportAlways( false )
1629 {
1630  nTab = nTableId;
1631  bEntirePara = false;
1632 }
1633 
1635 {
1636  return mbImportAlways || aSel.HasRange() || !aAltText.isEmpty() || IsTable();
1637 }
1638 
1640 {
1641  // set start position
1644  // adjust end position
1646  {
1649  }
1650 }
1651 
1653 {
1654  OSL_ENSURE( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
1655  ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
1656  "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
1657  // set end position
1658  aSel.nEndPara = rInfo.aSelection.nEndPara;
1659  aSel.nEndPos = rInfo.aSelection.nEndPos;
1660 }
1661 
1662 void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
1663 {
1664  // strip leading empty paragraphs
1665  while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
1666  {
1667  ++aSel.nStartPara;
1668  aSel.nStartPos = 0;
1669  }
1670  // strip trailing empty paragraphs
1671  while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
1672  {
1673  --aSel.nEndPara;
1674  aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
1675  }
1676 }
1677 
1685 class ScHTMLTableMap final
1686 {
1687 private:
1688  typedef std::shared_ptr< ScHTMLTable > ScHTMLTablePtr;
1689  typedef std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
1690 
1691 public:
1692  typedef ScHTMLTableStdMap::iterator iterator;
1693  typedef ScHTMLTableStdMap::const_iterator const_iterator;
1694 
1695 private:
1697  ScHTMLTableStdMap maTables;
1699 
1700 public:
1701  explicit ScHTMLTableMap( ScHTMLTable& rParentTable );
1702 
1703  const_iterator begin() const { return maTables.begin(); }
1704  const_iterator end() const { return maTables.end(); }
1705 
1709  ScHTMLTable* FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
1710 
1713  ScHTMLTable* CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText );
1714 
1715 private:
1717  void SetCurrTable( ScHTMLTable* pTable ) const
1718  { if( pTable ) mpCurrTable = pTable; }
1719 };
1720 
1722  mrParentTable(rParentTable),
1723  mpCurrTable(nullptr)
1724 {
1725 }
1726 
1728 {
1729  ScHTMLTable* pResult = nullptr;
1730  if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
1731  pResult = mpCurrTable; // cached table
1732  else
1733  {
1734  const_iterator aFind = maTables.find( nTableId );
1735  if( aFind != maTables.end() )
1736  pResult = aFind->second.get(); // table from this container
1737  }
1738 
1739  // not found -> search deep in nested tables
1740  if( !pResult && bDeep )
1741  for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
1742  pResult = aIter->second->FindNestedTable( nTableId );
1743 
1744  SetCurrTable( pResult );
1745  return pResult;
1746 }
1747 
1748 ScHTMLTable* ScHTMLTableMap::CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText )
1749 {
1750  ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText );
1751  maTables[ pTable->GetTableId() ].reset( pTable );
1752  SetCurrTable( pTable );
1753  return pTable;
1754 }
1755 
1756 namespace {
1757 
1764 class ScHTMLTableIterator
1765 {
1766 public:
1769  explicit ScHTMLTableIterator( const ScHTMLTableMap* pTableMap );
1770 
1771  bool is() const { return mpTableMap && maIter != maEnd; }
1772  ScHTMLTable* operator->() { return maIter->second.get(); }
1773  ScHTMLTableIterator& operator++() { ++maIter; return *this; }
1774 
1775 private:
1778  const ScHTMLTableMap* mpTableMap;
1779 };
1780 
1781 }
1782 
1783 ScHTMLTableIterator::ScHTMLTableIterator( const ScHTMLTableMap* pTableMap ) :
1784  mpTableMap(pTableMap)
1785 {
1786  if( pTableMap )
1787  {
1788  maIter = pTableMap->begin();
1789  maEnd = pTableMap->end();
1790  }
1791 }
1792 
1794  mnTableId( rnUnusedId ),
1795  mrnUnusedId( rnUnusedId )
1796 {
1797  ++mrnUnusedId;
1798 }
1799 
1800 ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const HtmlImportInfo& rInfo, bool bPreFormText ) :
1801  mpParentTable( &rParentTable ),
1802  maTableId( rParentTable.maTableId.mrnUnusedId ),
1803  maTableItemSet( rParentTable.GetCurrItemSet() ),
1804  mrEditEngine( rParentTable.mrEditEngine ),
1805  mrEEParseList( rParentTable.mrEEParseList ),
1806  mpCurrEntryVector( nullptr ),
1807  maSize( 1, 1 ),
1808  mpParser(rParentTable.mpParser),
1809  mbBorderOn( false ),
1810  mbPreFormText( bPreFormText ),
1811  mbRowOn( false ),
1812  mbDataOn( false ),
1813  mbPushEmptyLine( false ),
1814  mbCaptionOn ( false )
1815 {
1816  if( mbPreFormText )
1817  {
1818  ImplRowOn();
1819  ImplDataOn( ScHTMLSize( 1, 1 ) );
1820  }
1821  else
1822  {
1824  const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
1825  for (const auto& rOption : rOptions)
1826  {
1827  switch( rOption.GetToken() )
1828  {
1829  case HtmlOptionId::BORDER:
1830  mbBorderOn = rOption.GetString().isEmpty() || (rOption.GetNumber() != 0);
1831  break;
1832  case HtmlOptionId::ID:
1833  maTableName = rOption.GetString();
1834  break;
1835  default: break;
1836  }
1837  }
1838  }
1839 
1840  CreateNewEntry( rInfo );
1841 }
1842 
1844  SfxItemPool& rPool,
1845  EditEngine& rEditEngine,
1846  std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
1847  ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser
1848 ) :
1849  mpParentTable( nullptr ),
1850  maTableId( rnUnusedId ),
1851  maTableItemSet( rPool ),
1852  mrEditEngine( rEditEngine ),
1853  mrEEParseList( rEEParseList ),
1854  mpCurrEntryVector( nullptr ),
1855  maSize( 1, 1 ),
1856  mpParser(pParser),
1857  mbBorderOn( false ),
1858  mbPreFormText( false ),
1859  mbRowOn( false ),
1860  mbDataOn( false ),
1861  mbPushEmptyLine( false ),
1862  mbCaptionOn ( false )
1863 {
1864  // open the first "cell" of the document
1865  ImplRowOn();
1866  ImplDataOn( ScHTMLSize( 1, 1 ) );
1868 }
1869 
1871 {
1872 }
1873 
1875 {
1876  // first try cell item set, then row item set, then table item set
1878 }
1879 
1881 {
1882  ScHTMLSize aSpan( 1, 1 );
1883  const ScRange* pRange = maVMergedCells.Find( rCellPos.MakeAddr() );
1884  if (!pRange)
1885  pRange = maHMergedCells.Find( rCellPos.MakeAddr() );
1886  if (pRange)
1887  aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
1888  return aSpan;
1889 }
1890 
1892 {
1893  return mxNestedTables ? mxNestedTables->FindTable( nTableId ) : nullptr;
1894 }
1895 
1896 void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
1897 {
1898  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutItem - no current entry" );
1899  if( mxCurrEntry && mxCurrEntry->IsEmpty() )
1900  mxCurrEntry->GetItemSet().Put( rItem );
1901 }
1902 
1904 {
1905  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutText - no current entry" );
1906  if( mxCurrEntry )
1907  {
1908  if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
1909  mxCurrEntry->AdjustStart( rInfo );
1910  else
1911  mxCurrEntry->AdjustEnd( rInfo );
1912  if (mbCaptionOn)
1913  maCaptionBuffer.append(rInfo.aText);
1914 
1915  }
1916 }
1917 
1919 {
1920  if( mxCurrEntry && mbDataOn && !IsEmptyCell() )
1921  mxCurrEntry->SetImportAlways();
1922  PushEntry( rInfo );
1923  CreateNewEntry( rInfo );
1925 }
1926 
1928 {
1929  // empty line, if <br> is at start of cell
1931 }
1932 
1934 {
1935  // call directly, InsertPara() has not been called before
1937 }
1938 
1940 {
1941  // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
1943 }
1944 
1946 {
1947  OSL_ENSURE( mxCurrEntry, "ScHTMLTable::AnchorOn - no current entry" );
1948  // don't skip entries with single hyperlinks
1949  if( mxCurrEntry )
1950  mxCurrEntry->SetImportAlways();
1951 }
1952 
1954 {
1955  PushEntry( rInfo );
1956  return InsertNestedTable( rInfo, false );
1957 }
1958 
1960 {
1961  return mbPreFormText ? this : CloseTable( rInfo );
1962 }
1963 
1965 {
1966  mbCaptionOn = true;
1967  maCaptionBuffer.setLength(0);
1968 }
1969 
1971 {
1972  if (!mbCaptionOn)
1973  return;
1974  maCaption = maCaptionBuffer.makeStringAndClear().trim();
1975  mbCaptionOn = false;
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  moDataItemSet->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  moRowItemSet.emplace( maTableItemSet );
2444  maCurrCell.mnCol = 0;
2445  mbRowOn = true;
2446  mbDataOn = false;
2447 }
2448 
2450 {
2451  if( mbDataOn )
2452  ImplDataOff();
2453  if( mbRowOn )
2454  {
2455  moRowItemSet.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  moDataItemSet.emplace( *moRowItemSet );
2468  InsertNewCell( rSpanSize );
2469  mbDataOn = true;
2470  mbPushEmptyLine = false;
2471 }
2472 
2474 {
2475  if( mbDataOn )
2476  {
2477  moDataItemSet.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::CAPTION_ON: mpCurrTable->CaptionOn(); break; // <caption>
2852  case HtmlTokenId::CAPTION_OFF: mpCurrTable->CaptionOff(); break; // </caption>
2853  case HtmlTokenId::TABLEROW_ON: mpCurrTable->RowOn( rInfo ); break; // <tr>
2854  case HtmlTokenId::TABLEROW_OFF: mpCurrTable->RowOff( rInfo ); break; // </tr>
2855  case HtmlTokenId::TABLEHEADER_ON: // <th>
2856  case HtmlTokenId::TABLEDATA_ON: mpCurrTable->DataOn( rInfo ); break; // <td>
2857  case HtmlTokenId::TABLEHEADER_OFF: // </th>
2858  case HtmlTokenId::TABLEDATA_OFF: mpCurrTable->DataOff( rInfo ); break; // </td>
2859  case HtmlTokenId::PREFORMTXT_ON: PreOn( rInfo ); break; // <pre>
2860  case HtmlTokenId::PREFORMTXT_OFF: PreOff( rInfo ); break; // </pre>
2861 
2862 // --- formatting ---
2863  case HtmlTokenId::FONT_ON: FontOn( rInfo ); break; // <font>
2864 
2865  case HtmlTokenId::BIGPRINT_ON: // <big>
2868  break;
2869  case HtmlTokenId::SMALLPRINT_ON: // <small>
2872  break;
2873 
2874  case HtmlTokenId::BOLD_ON: // <b>
2875  case HtmlTokenId::STRONG_ON: // <strong>
2877  break;
2878 
2879  case HtmlTokenId::ITALIC_ON: // <i>
2880  case HtmlTokenId::EMPHASIS_ON: // <em>
2881  case HtmlTokenId::ADDRESS_ON: // <address>
2882  case HtmlTokenId::BLOCKQUOTE_ON: // <blockquote>
2883  case HtmlTokenId::BLOCKQUOTE30_ON: // <bq>
2884  case HtmlTokenId::CITATION_ON: // <cite>
2885  case HtmlTokenId::VARIABLE_ON: // <var>
2887  break;
2888 
2889  case HtmlTokenId::DEFINSTANCE_ON: // <dfn>
2892  break;
2893 
2894  case HtmlTokenId::UNDERLINE_ON: // <u>
2896  break;
2897  default: break;
2898  }
2899 }
2900 
2902 {
2903  mpCurrTable->PutText( rInfo );
2904  if( mbTitleOn )
2905  maTitle.append(rInfo.aText);
2906 }
2907 
2909 {
2910  const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2911  for (const auto& rOption : rOptions)
2912  {
2913  switch( rOption.GetToken() )
2914  {
2915  case HtmlOptionId::FACE :
2916  {
2917  const OUString& rFace = rOption.GetString();
2918  OUString aFontName;
2919  sal_Int32 nPos = 0;
2920  while( nPos != -1 )
2921  {
2922  // font list separator: VCL = ';' HTML = ','
2923  OUString aFName = comphelper::string::strip(rFace.getToken(0, ',', nPos), ' ');
2924  aFontName = ScGlobal::addToken(aFontName, aFName, ';');
2925  }
2926  if ( !aFontName.isEmpty() )
2928  aFontName, EMPTY_OUSTRING, PITCH_DONTKNOW,
2929  RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
2930  }
2931  break;
2932  case HtmlOptionId::SIZE :
2933  {
2934  sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( rOption.GetNumber(), 1, SC_HTML_FONTSIZES );
2936  }
2937  break;
2938  case HtmlOptionId::COLOR :
2939  {
2940  Color aColor;
2941  rOption.GetColor( aColor );
2943  }
2944  break;
2945  default: break;
2946  }
2947  }
2948 }
2949 
2951 {
2952  if( mpDoc->GetDocumentShell() )
2953  {
2954  HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
2955 
2956  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
2957  mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
2958  pParser->ParseMetaOptions(
2959  xDPS->getDocumentProperties(),
2961  }
2962 }
2963 
2965 {
2966  mbTitleOn = true;
2967  maTitle.setLength(0);
2968 }
2969 
2971 {
2972  if( !mbTitleOn )
2973  return;
2974 
2975  OUString aTitle = maTitle.makeStringAndClear().trim();
2976  if (!aTitle.isEmpty() && mpDoc->GetDocumentShell())
2977  {
2978  uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
2979  mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
2980 
2981  xDPS->getDocumentProperties()->setTitle(aTitle);
2982  }
2983  InsertText( rInfo );
2984  mbTitleOn = false;
2985 }
2986 
2988 {
2989  mpCurrTable = mpCurrTable->TableOn( rInfo );
2990 }
2991 
2993 {
2994  mpCurrTable = mpCurrTable->TableOff( rInfo );
2995 }
2996 
2998 {
2999  mpCurrTable = mpCurrTable->PreOn( rInfo );
3000 }
3001 
3003 {
3004  mpCurrTable = mpCurrTable->PreOff( rInfo );
3005 }
3006 
3008 {
3009  mpCurrTable = mpCurrTable->CloseTable( rInfo );
3010 }
3011 
3012 namespace {
3013 
3017 class CSSHandler: public orcus::css_handler
3018 {
3019  struct MemStr
3020  {
3021  const char* mp;
3022  size_t mn;
3023 
3024  MemStr() : mp(nullptr), mn(0) {}
3025  MemStr(const char* p, size_t n) : mp(p), mn(n) {}
3026  MemStr(const MemStr& r) : mp(r.mp), mn(r.mn) {}
3027  MemStr& operator=(const MemStr& r) = default;
3028  };
3029 
3030  typedef std::pair<MemStr, MemStr> SelectorName; // element : class
3031  typedef std::vector<SelectorName> SelectorNames;
3032 
3033  SelectorNames maSelectorNames; // current selector names
3034  MemStr maPropName; // current property name.
3035  MemStr maPropValue; // current property value.
3036  ScHTMLStyles& mrStyles;
3037 
3038 public:
3039  explicit CSSHandler(ScHTMLStyles& rStyles):
3040  maPropName(),
3041  maPropValue(),
3042  mrStyles(rStyles)
3043  {}
3044 
3045  // selector name starting with "@"
3046  static void at_rule_name(const char* /*p*/, size_t /*n*/)
3047  {
3048  // TODO: For now, we ignore at-rule properties
3049  }
3050 
3051  // selector name not starting with "." or "#" (i.e. element selectors)
3052  void simple_selector_type(const char* pElem, size_t nElem)
3053  {
3054  MemStr aElem(pElem, nElem); // element given
3055  MemStr aClass(nullptr, 0); // class name not given - to be added in the "element global" storage
3056  SelectorName aName(aElem, aClass);
3057 
3058  maSelectorNames.push_back(aName);
3059  }
3060 
3061  // selector names starting with a "." (i.e. class selector)
3062  void simple_selector_class(const char* pClassName, size_t nClassName)
3063  {
3064  MemStr aElem(nullptr, 0); // no element given - should be added in the "global" storage
3065  MemStr aClass(pClassName, nClassName);
3066  SelectorName aName(aElem, aClass);
3067 
3068  maSelectorNames.push_back(aName);
3069  }
3070 
3071  // TODO: Add other selectors
3072 
3073  void property_name(const char* p, size_t n)
3074  {
3075  maPropName = MemStr(p, n);
3076  }
3077 
3078  void value(const char* p, size_t n)
3079  {
3080  maPropValue = MemStr(p, n);
3081  }
3082 
3083  void end_block() {
3084  maSelectorNames.clear();
3085  }
3086 
3087  void end_property()
3088  {
3089  SelectorNames::const_iterator itr = maSelectorNames.begin(), itrEnd = maSelectorNames.end();
3090  for (; itr != itrEnd; ++itr)
3091  {
3092  // Add this property to the collection for each selector.
3093  const SelectorName& rSelName = *itr;
3094  const MemStr& rElem = rSelName.first;
3095  const MemStr& rClass = rSelName.second;
3096  OUString aName(maPropName.mp, maPropName.mn, RTL_TEXTENCODING_UTF8);
3097  OUString aValue(maPropValue.mp, maPropValue.mn, RTL_TEXTENCODING_UTF8);
3098  mrStyles.add(rElem.mp, rElem.mn, rClass.mp, rClass.mn, aName, aValue);
3099  }
3100  maPropName = MemStr();
3101  maPropValue = MemStr();
3102  }
3103 
3104 };
3105 
3106 }
3107 
3108 void ScHTMLQueryParser::ParseStyle(std::u16string_view rStrm)
3109 {
3110  OString aStr = OUStringToOString(rStrm, RTL_TEXTENCODING_UTF8);
3111  CSSHandler aHdl(GetStyles());
3112  orcus::css_parser<CSSHandler> aParser(aStr.getStr(), aStr.getLength(), aHdl);
3113  try
3114  {
3115  aParser.parse();
3116  }
3117  catch (const orcus::css::parse_error& rOrcusParseError)
3118  {
3119  SAL_WARN("sc", "ScHTMLQueryParser::ParseStyle: " << rOrcusParseError.what());
3120  // TODO: Parsing of CSS failed. Do nothing for now.
3121  }
3122 }
3123 
3124 IMPL_LINK( ScHTMLQueryParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
3125 {
3126  switch( rInfo.eState )
3127  {
3128  case HtmlImportState::Start:
3129  break;
3130 
3131  case HtmlImportState::NextToken:
3132  ProcessToken( rInfo );
3133  break;
3134 
3135  case HtmlImportState::InsertPara:
3136  mpCurrTable->InsertPara( rInfo );
3137  break;
3138 
3139  case HtmlImportState::SetAttr:
3140  case HtmlImportState::InsertText:
3141  case HtmlImportState::InsertField:
3142  break;
3143 
3144  case HtmlImportState::End:
3145  while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
3146  CloseTable( rInfo );
3147  break;
3148 
3149  default:
3150  OSL_FAIL( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
3151  }
3152 }
3153 
3154 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString maCaption
Table name from.
Definition: htmlpars.hxx:526
std::unique_ptr< OuterMap > pTables
Definition: htmlpars.hxx:160
void RowOn(const HtmlImportInfo &rInfo)
Starts next row (tag).
Definition: htmlpars.cxx:1989
EditEngine * pEdit
Definition: eeparser.hxx:102
EditEngine & mrEditEngine
List of all used cells.
Definition: htmlpars.hxx:535
Type
SvxCellHorJustify
ScHTMLStyles()
just a persistent empty string.
Definition: htmlpars.cxx:72
std::vector< std::shared_ptr< ScEEParseEntry > > & mrEEParseList
Edit engine (from ScEEParser).
Definition: htmlpars.hxx:536
SCCOL mnCols
Definition: htmlpars.hxx:262
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:1382
SfxItemSet maTableItemSet
Unique identifier of this table.
Definition: htmlpars.hxx:529
const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_LARGE
Definition: htmlpars.hxx:38
sal_Int32 nIndex
const char nHorizontal
Definition: eeparser.hxx:34
ScAddress aStart
Definition: address.hxx:499
static void EntryEnd(ScEEParseEntry *, const ESelection &)
Definition: htmlpars.cxx:316
#define OOO_STRING_SVTOOLS_HTML_VA_bottom
ScHTMLSize maSize
Cumulated cell counts for each HTML table column/row.
Definition: htmlpars.hxx:541
ScDocument * mpDoc
Definition: htmlpars.hxx:82
static void MakeCol(ScHTMLColOffset *, sal_uInt16 &nOffset, sal_uInt16 &nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol)
Definition: htmlpars.cxx:368
void SetHtmlImportHdl(const Link< HtmlImportInfo &, void > &rLink)
#define EMPTY_OUSTRING
Definition: global.hxx:214
ScHTMLTableAutoId(ScHTMLTableId &rnUnusedId)
Reference to global unused identifier variable.
Definition: htmlpars.cxx:1793
static void ModifyOffset(ScHTMLColOffset *, sal_uInt16 &nOldOffset, sal_uInt16 &nNewOffset, sal_uInt16 nOffsetTol)
Definition: htmlpars.cxx:402
A map of ScHTMLTable objects.
Definition: htmlpars.cxx:1685
FormulaCommand pE
constexpr TypedWhichId< SvxBoxItem > ATTR_BORDER(150)
virtual ~ScHTMLParser() override
Definition: htmlpars.cxx:200
void Strip(const EditEngine &rEditEngine)
Deletes leading and trailing empty paragraphs from the entry.
Definition: htmlpars.cxx:1662
SCROW Row() const
Definition: address.hxx:261
bool mbBorderOn
Definition: htmlpars.hxx:545
void setWidth(tools::Long nWidth)
OUString aText
SCCOL nColCnt
Definition: eeparser.hxx:109
A single entry containing a line of text or representing a table.
Definition: htmlpars.hxx:272
#define OOO_STRING_SVTOOLS_HTML_AL_center
bool PushEntry(ScHTMLEntryPtr &rxEntry)
Tries to insert the entry into the current cell.
Definition: htmlpars.cxx:2307
void NewActEntry(const ScEEParseEntry *)
Definition: eeimpars.cxx:646
ScHTMLTableStdMap::const_iterator const_iterator
Definition: htmlpars.cxx:1693
const sal_uInt32 SC_HTML_FONTSIZES
Definition: htmlpars.hxx:34
ScHTMLEntry(const SfxItemSet &rItemSet, ScHTMLTableId nTableId=SC_HTML_NO_TABLE)
Definition: htmlpars.cxx:1626
OString strip(std::string_view rIn, char c)
ESelection aSel
Definition: eeparser.hxx:55
void Colonize(ScEEParseEntry *)
Definition: htmlpars.cxx:780
::std::map< SCROW, SCROW > InnerMap
Definition: htmlpars.hxx:145
bool Intersects(const ScRange &rRange) const
Definition: address.cxx:1558
bool IsEmptyCell() const
Returns true, if the current cell does not contain an entry yet.
Definition: htmlpars.cxx: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:149
OUString maTableName
Table of nested HTML tables.
Definition: htmlpars.hxx:525
ScRangeList maHMergedCells
Items for the current cell.
Definition: htmlpars.hxx:532
sal_uIntPtr sal_uLong
std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap
Definition: htmlpars.cxx:1689
long Long
void CloseEntry(const HtmlImportInfo *)
Definition: htmlpars.cxx:808
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:1652
bool IsAtBeginningOfText(const HtmlImportInfo *)
Definition: htmlpars.cxx:1392
ScHTMLTable * TableOn(const HtmlImportInfo &rInfo)
Starts a new table nested in this table (.
Definition: htmlpars.cxx:1953
OUString GetImportFormatName(sal_uInt16 nFormat)
ScHTMLTable * TableOff(const HtmlImportInfo &rInfo)
Closes this table (tag).
Definition: htmlpars.cxx:1959
const SCCOL SCCOL_MAX
Definition: address.hxx:56
NamePropsType m_GlobalProps
Definition: htmlpars.hxx:54
bool mbPreFormText
true = Table borders on.
Definition: htmlpars.hxx:546
sal_Int64 n
css::uno::Reference< css::frame::XModel3 > GetModel() const
const OUString & GetString() const
SCROW nRowMax
Definition: eeparser.hxx:112
aBuf
virtual ~ScHTMLTable()
Definition: htmlpars.cxx:1870
ScSizeVec maCumSizes[2]
Working entry, not yet inserted in a list.
Definition: htmlpars.hxx:540
void InsertText(const HtmlImportInfo &rInfo)
Inserts a text portion into current entry.
Definition: htmlpars.cxx:2901
const_iterator find(const Value &x) const
void CloseTable(const HtmlImportInfo &rInfo)
Closes the current table, regardless on opening tag.
Definition: htmlpars.cxx:3007
ScAddress aEnd
Definition: address.hxx:500
SCCOLROW GetDocSize(ScHTMLOrient eOrient, SCCOLROW nCellPos) const
Returns the resulting document row/column count of the specified HTML row/column. ...
Definition: htmlpars.cxx:2174
void ProcToken(HtmlImportInfo *)
Definition: htmlpars.cxx:1455
constexpr TypedWhichId< SvxFontItem > ATTR_FONT(100)
SCROW mnRow
Definition: htmlpars.hxx:237
::std::map< OUString, PropsType > NamePropsType
Definition: htmlpars.hxx:51
virtual ~ScHTMLGlobalTable() override
Definition: htmlpars.cxx:2745
ScHTMLEntryPtr mxCurrEntry
Current entry vector from map for faster access.
Definition: htmlpars.hxx:539
ScHTMLTableId & mrnUnusedId
The created unique table identifier.
Definition: htmlpars.hxx:310
SotClipboardFormatId & operator++(SotClipboardFormatId &eFormat)
SC_DLLPUBLIC void ApplyAttr(SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem &rAttr)
Definition: document.cxx:4788
ScDocument & GetDoc()
Definition: htmlpars.hxx:91
const SCROW SCROW_MAX
Definition: address.hxx:55
ScHTMLParser * mpParser
Resulting base address in a Calc document.
Definition: htmlpars.hxx:544
void ParseStyle(std::u16string_view rStrm)
Definition: htmlpars.cxx:3108
void NextRow(const HtmlImportInfo *)
Definition: htmlpars.cxx:332
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:538
const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_SMALL
Definition: htmlpars.hxx:37
virtual ~ScHTMLQueryParser() override
Definition: htmlpars.cxx:2769
bool mbDataOn
true = Inside of .
Definition: htmlpars.hxx:548
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:983
static OutputDevice * GetDefaultDevice()
WEIGHT_BOLD
constexpr tools::Long Width() const
const ScHTMLTableId SC_HTML_NO_TABLE
Used as table index for normal (non-table) entries in ScHTMLEntry structs.
Definition: htmlpars.hxx:231
SvParser< HtmlTokenId > * pParser
sal_uInt16 nTableWidth
Definition: htmlpars.hxx:169
static void MakeColNoRef(ScHTMLColOffset *, sal_uInt16 nOffset, sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol)
Definition: htmlpars.cxx:386
void MetaOn(const HtmlImportInfo &rInfo)
Processes the tag.
Definition: htmlpars.cxx:2950
std::map< ScHTMLPos, ScHTMLEntryVector > maEntryMap
List that owns the parse entries (from ScEEParser).
Definition: htmlpars.hxx:537
virtual const ScHTMLTable * GetGlobalTable() const override
Returns the "global table" which contains the entire HTML document.
Definition: htmlpars.cxx:296
sal_uInt16 nTable
Definition: htmlpars.hxx:165
sal_uInt16 sal_Unicode
sal_uInt16 ScHTMLTableId
Type for a unique identifier for each table.
Definition: htmlpars.hxx:227
ScHTMLTable & mrParentTable
Definition: htmlpars.cxx:1696
void TableRowOff(const HtmlImportInfo *)
Definition: htmlpars.cxx:990
ScRangeList maUsedCells
List of all vertically merged cells.
Definition: htmlpars.hxx:534
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:66
void CaptionOff()
Processes the caption of the table ( tag).
Definition: htmlpars.cxx:1970
ScHTMLLayoutParser(EditEngine *, const OUString &rBaseURL, const Size &aPageSize, ScDocument *)
Definition: htmlpars.cxx:204
ScHTMLTable * CreateTable(const HtmlImportInfo &rInfo, bool bPreFormText)
Inserts a new table into the container.
Definition: htmlpars.cxx:1748
bool PutEntry(OUString &rString, sal_Int32 &nCheckPos, SvNumFormatType &nType, sal_uInt32 &nKey, LanguageType eLnge=LANGUAGE_DONTKNOW, bool bReplaceBooleanEquivalent=true)
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
ScHTMLGlobalTablePtr mxGlobTable
The title of the document.
Definition: htmlpars.hxx:620
SCROW nRowCnt
Definition: eeparser.hxx:110
constexpr sal_uInt32 NUMBERFORMAT_ENTRY_NOT_FOUND
o3tl::sorted_vector< sal_uLong > ScHTMLColOffset
Definition: htmlpars.hxx:97
bool bEntirePara
Definition: eeparser.hxx:73
int nCount
void Recalc()
Recalculates sizes and resulting positions of all document entries.
Definition: htmlpars.cxx: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:119
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:1149
::std::vector< ScHTMLEntry * > ScHTMLEntryVector
Definition: htmlpars.hxx:451
ScHTMLTable(ScHTMLTable &rParentTable, const HtmlImportInfo &rInfo, bool bPreFormText)
Creates a new HTML table without content.
Definition: htmlpars.cxx:1800
SCTAB Tab() const
Definition: address.hxx:270
#define OOO_STRING_SVTOOLS_HTML_VA_top
sal_uInt16 nWidth
Definition: eeparser.hxx:71
void SetRow(SCROW nRowP)
Definition: address.hxx:274
#define OOO_STRING_SVTOOLS_HTML_META_content_type
void RecalcDocPos(const ScHTMLPos &rBasePos)
Recalculates the position of all cell entries and nested tables.
Definition: htmlpars.cxx:2656
bool mbImportAlways
Definition: htmlpars.hxx:303
bool mbPushEmptyLine
true = Inside of or .
Definition: htmlpars.hxx:549
::std::stack< std::unique_ptr< ScHTMLTableStackEntry > > aTableStack
Definition: htmlpars.hxx:157
SCROW nRowOverlap
Definition: eeparser.hxx:69
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
sal_Int32 nEndPos
const ScRange * Find(const ScAddress &) const
Definition: rangelst.cxx:1039
size_type size() const
std::unique_ptr< Graphic > pGraphic
Definition: eeparser.hxx:44
sal_Int32 GetTextLen() const
void SetCol(SCCOL nColP)
Definition: address.hxx:278
ErrCode Read(SvStream &rInput, const OUString &rBaseURL, EETextFormat, SvKeyValueIterator *pHTTPHeaderAttrs=nullptr)
void HeadingOn()
Inserts a heading line (.
Definition: htmlpars.cxx:1933
const Link< HtmlImportInfo &, void > & GetHtmlImportHdl() const
constexpr TypedWhichId< SvxUnderlineItem > ATTR_FONT_UNDERLINE(104)
sal_uInt16 nOffset
Definition: eeparser.hxx:70
void Image(HtmlImportInfo *)
Definition: htmlpars.cxx:1256
ScHTMLTableId GetTableId() const
Returns the unique identifier of the table.
Definition: htmlpars.hxx:345
sal_uInt16 nColOffset
Definition: htmlpars.hxx:170
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:1721
ScHTMLStyles & GetStyles()
Definition: htmlpars.hxx:90
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:871
constexpr TypedWhichId< SvxPostureItem > ATTR_FONT_POSTURE(103)
T * get() const
SC_DLLPUBLIC SvNumberFormatter * GetFormatTable() const
Definition: documen2.cxx:441
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:74
const char nVertical
Definition: eeparser.hxx:35
int i
void RowOff(const HtmlImportInfo &rInfo)
Closes the current row (tag).
Definition: htmlpars.cxx:2000
virtual ~ScHTMLLayoutParser() override
Definition: htmlpars.cxx:231
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:303
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:105
sal_Int16 SCCOL
Definition: types.hxx:21
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:874
OUStringBuffer maTitle
Definition: htmlpars.hxx:619
constexpr TypedWhichId< SvxVerJustifyItem > ATTR_VER_JUSTIFY(132)
ScHTMLPos maCurrCell
Size of the table.
Definition: htmlpars.hxx:542
bool mbRowOn
true = Table from preformatted text (
Definition: htmlpars.hxx:547
void DataOff(const HtmlImportInfo &rInfo)
Closes the current cell ( or tag).
Definition: htmlpars.cxx:2124
Point aSpace
Definition: eeparser.hxx:41
void PutItem(const SfxPoolItem &rItem)
Puts the item into the item set of the current entry.
Definition: htmlpars.cxx:1896
void SetCurrTable(ScHTMLTable *pTable) const
Sets a working table with its index for search optimization.
Definition: htmlpars.cxx:1717
ScHTMLTableId mnUnusedId
Pointer to current table (performance).
Definition: htmlpars.hxx:622
sal_uInt16 GetWidthPixel(const HTMLOption &)
Definition: htmlpars.cxx:1362
ColWidthsMap maColWidths
Definition: eeparser.hxx:107
void ProcessToken(const HtmlImportInfo &rInfo)
Handles all possible tags in the HTML document.
Definition: htmlpars.cxx:2816
sal_uInt16 nOffsetTolerance
Definition: htmlpars.hxx:172
ScHTMLColOffset * pLocalColOffset
Definition: htmlpars.hxx:162
ScHTMLTable * mpParentTable
Definition: htmlpars.hxx:523
size_t size() const
Definition: rangelst.hxx:89
#define OOO_STRING_SVTOOLS_HTML_AL_left
static bool IsSpaceCharInfo(const HtmlImportInfo &rInfo)
Returns true, if import info represents a space character.
Definition: htmlpars.cxx:2282
LINESTYLE_SINGLE
The HTML parser for data queries.
Definition: htmlpars.hxx:574
virtual bool ParseMetaOptions(const css::uno::Reference< css::document::XDocumentProperties > &, SvKeyValueIterator *)
Collection of HTML style data parsed from the content of