Version 5.2.6.1, tag libreoffice-5.2.6.1
[LibreOffice.git] / sc / source / filter / html / htmlpars.cxx
bloba094f04512f63f61b1b4374fd18271991887a5c2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <comphelper/string.hxx>
24 #include "scitems.hxx"
25 #include <editeng/eeitem.hxx>
27 #include <svtools/htmlcfg.hxx>
28 #include <svx/algitem.hxx>
29 #include <editeng/colritem.hxx>
30 #include <editeng/brushitem.hxx>
31 #include <editeng/editeng.hxx>
32 #include <editeng/fhgtitem.hxx>
33 #include <editeng/fontitem.hxx>
34 #include <editeng/postitem.hxx>
35 #include <editeng/udlnitem.hxx>
36 #include <editeng/wghtitem.hxx>
37 #include <editeng/boxitem.hxx>
38 #include <editeng/justifyitem.hxx>
39 #include <sfx2/objsh.hxx>
40 #include <svl/eitem.hxx>
41 #include <svl/intitem.hxx>
42 #include <vcl/graphicfilter.hxx>
43 #include <svtools/parhtml.hxx>
44 #include <svtools/htmlkywd.hxx>
45 #include <svtools/htmltokn.h>
46 #include <sfx2/docfile.hxx>
48 #include <vcl/svapp.hxx>
49 #include <tools/urlobj.hxx>
50 #include <tools/tenccvt.hxx>
52 #include <rtl/tencinfo.h>
54 #include "htmlpars.hxx"
55 #include "global.hxx"
56 #include "document.hxx"
57 #include "rangelst.hxx"
59 #include <config_orcus.h>
60 #include <o3tl/make_unique.hxx>
61 #if ENABLE_ORCUS
62 #include <orcus/css_parser.hpp>
63 #endif
65 #include <com/sun/star/document/XDocumentProperties.hpp>
66 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
67 #include <utility>
68 #include <o3tl/make_unique.hxx>
70 using ::editeng::SvxBorderLine;
71 using namespace ::com::sun::star;
73 ScHTMLStyles::ScHTMLStyles() : maEmpty() {}
75 void ScHTMLStyles::add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
76 const OUString& aProp, const OUString& aValue)
78 if (pElemName)
80 OUString aElem(pElemName, nElemName, RTL_TEXTENCODING_UTF8);
81 aElem = aElem.toAsciiLowerCase();
82 if (pClassName)
84 // Both element and class names given.
85 ElemsType::iterator itrElem = m_ElemProps.find(aElem);
86 if (itrElem == m_ElemProps.end())
88 // new element
89 std::pair<ElemsType::iterator, bool> r =
90 m_ElemProps.insert(std::make_pair(aElem, o3tl::make_unique<NamePropsType>()));
91 if (!r.second)
92 // insertion failed.
93 return;
94 itrElem = r.first;
97 NamePropsType *const pClsProps = itrElem->second.get();
98 OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
99 aClass = aClass.toAsciiLowerCase();
100 insertProp(*pClsProps, aClass, aProp, aValue);
102 else
104 // Element name only. Add it to the element global.
105 insertProp(m_ElemGlobalProps, aElem, aProp, aValue);
108 else
110 if (pClassName)
112 // Class name only. Add it to the global.
113 OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
114 aClass = aClass.toAsciiLowerCase();
115 insertProp(m_GlobalProps, aClass, aProp, aValue);
120 const OUString& ScHTMLStyles::getPropertyValue(
121 const OUString& rElem, const OUString& rClass, const OUString& rPropName) const
123 // First, look into the element-class storage.
125 auto const itr = m_ElemProps.find(rElem);
126 if (itr != m_ElemProps.end())
128 const NamePropsType *const pClasses = itr->second.get();
129 NamePropsType::const_iterator itr2 = pClasses->find(rClass);
130 if (itr2 != pClasses->end())
132 const PropsType *const pProps = itr2->second.get();
133 PropsType::const_iterator itr3 = pProps->find(rPropName);
134 if (itr3 != pProps->end())
135 return itr3->second;
139 // Next, look into the class global storage.
141 auto const itr = m_GlobalProps.find(rClass);
142 if (itr != m_GlobalProps.end())
144 const PropsType *const pProps = itr->second.get();
145 PropsType::const_iterator itr2 = pProps->find(rPropName);
146 if (itr2 != pProps->end())
147 return itr2->second;
150 // As the last resort, look into the element global storage.
152 auto const itr = m_ElemGlobalProps.find(rClass);
153 if (itr != m_ElemGlobalProps.end())
155 const PropsType *const pProps = itr->second.get();
156 PropsType::const_iterator itr2 = pProps->find(rPropName);
157 if (itr2 != pProps->end())
158 return itr2->second;
162 return maEmpty; // nothing found.
165 void ScHTMLStyles::insertProp(
166 NamePropsType& rStore, const OUString& aName,
167 const OUString& aProp, const OUString& aValue)
169 NamePropsType::iterator itr = rStore.find(aName);
170 if (itr == rStore.end())
172 // new element
173 std::pair<NamePropsType::iterator, bool> r =
174 rStore.insert(std::make_pair(aName, o3tl::make_unique<PropsType>()));
175 if (!r.second)
176 // insertion failed.
177 return;
179 itr = r.first;
182 PropsType *const pProps = itr->second.get();
183 pProps->insert(PropsType::value_type(aProp, aValue));
186 // BASE class for HTML parser classes
188 ScHTMLParser::ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
189 ScEEParser( pEditEngine ),
190 mpDoc( pDoc )
192 SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
193 for( sal_uInt16 nIndex = 0; nIndex < SC_HTML_FONTSIZES; ++nIndex )
194 maFontHeights[ nIndex ] = rHtmlOptions.GetFontSize( nIndex ) * 20;
197 ScHTMLParser::~ScHTMLParser()
201 ScHTMLLayoutParser::ScHTMLLayoutParser(
202 EditEngine* pEditP, const OUString& rBaseURL, const Size& aPageSizeP,
203 ScDocument* pDocP ) :
204 ScHTMLParser( pEditP, pDocP ),
205 aPageSize( aPageSizeP ),
206 aBaseURL( rBaseURL ),
207 xLockedList( new ScRangeList ),
208 pTables( nullptr ),
209 pColOffset( new ScHTMLColOffset ),
210 pLocalColOffset( new ScHTMLColOffset ),
211 nFirstTableCell(0),
212 nTableLevel(0),
213 nTable(0),
214 nMaxTable(0),
215 nColCntStart(0),
216 nMaxCol(0),
217 nTableWidth(0),
218 nColOffset(0),
219 nColOffsetStart(0),
220 nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
221 bTabInTabCell( false ),
222 bFirstRow( true ),
223 bInCell( false ),
224 bInTitle( false )
226 MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
227 MakeColNoRef( pColOffset, 0, 0, 0, 0 );
230 ScHTMLLayoutParser::~ScHTMLLayoutParser()
232 while ( !aTableStack.empty() )
234 ScHTMLTableStackEntry* pS = aTableStack.top();
235 aTableStack.pop();
237 bool found = false;
238 for (ScEEParseEntry* p : maList)
240 if ( pS->pCellEntry == p )
242 found = true;
243 break;
246 if ( !found )
247 delete pS->pCellEntry;
248 if ( pS->pLocalColOffset != pLocalColOffset )
249 delete pS->pLocalColOffset;
250 delete pS;
252 delete pLocalColOffset;
253 delete pColOffset;
254 if ( pTables )
256 for( OuterMap::const_iterator it = pTables->begin(); it != pTables->end(); ++it)
257 delete it->second;
258 delete pTables;
262 sal_uLong ScHTMLLayoutParser::Read( SvStream& rStream, const OUString& rBaseURL )
264 Link<ImportInfo&,void> aOldLink = pEdit->GetImportHdl();
265 pEdit->SetImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
267 SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
268 bool bLoading = pObjSh && pObjSh->IsLoading();
270 SvKeyValueIteratorRef xValues;
271 SvKeyValueIterator* pAttributes = nullptr;
272 if ( bLoading )
273 pAttributes = pObjSh->GetHeaderAttributes();
274 else
276 // When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
277 // (used when pasting from clipboard)
278 const sal_Char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
279 if( pCharSet )
281 OUString aContentType = "text/html; charset=";
282 aContentType += OUString::createFromAscii( pCharSet );
284 xValues = new SvKeyValueIterator;
285 xValues->Append( SvKeyValue( OUString( OOO_STRING_SVTOOLS_HTML_META_content_type ), aContentType ) );
286 pAttributes = xValues;
290 sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_HTML, pAttributes );
292 pEdit->SetImportHdl( aOldLink );
293 // Create column width
294 Adjust();
295 OutputDevice* pDefaultDev = Application::GetDefaultDevice();
296 sal_uInt16 nCount = pColOffset->size();
297 sal_uLong nOff = (*pColOffset)[0];
298 Size aSize;
299 for ( sal_uInt16 j = 1; j < nCount; j++ )
301 aSize.Width() = (*pColOffset)[j] - nOff;
302 aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MAP_TWIP ) );
303 maColWidths[ j-1 ] = aSize.Width();
304 nOff = (*pColOffset)[j];
306 return nErr;
309 const ScHTMLTable* ScHTMLLayoutParser::GetGlobalTable() const
311 return nullptr;
314 void ScHTMLLayoutParser::NewActEntry( ScEEParseEntry* pE )
316 ScEEParser::NewActEntry( pE );
317 if ( pE )
319 if ( !pE->aSel.HasRange() )
320 { // Completely empty, following text ends up in the same paragraph!
321 pActEntry->aSel.nStartPara = pE->aSel.nEndPara;
322 pActEntry->aSel.nStartPos = pE->aSel.nEndPos;
325 pActEntry->aSel.nEndPara = pActEntry->aSel.nStartPara;
326 pActEntry->aSel.nEndPos = pActEntry->aSel.nStartPos;
329 void ScHTMLLayoutParser::EntryEnd( ScEEParseEntry* pE, const ESelection& rSel )
331 if ( rSel.nEndPara >= pE->aSel.nStartPara )
333 pE->aSel.nEndPara = rSel.nEndPara;
334 pE->aSel.nEndPos = rSel.nEndPos;
336 else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
337 { // Did not attach a paragraph, but empty, do nothing
339 else
341 OSL_FAIL( "EntryEnd: EditEngine ESelection End < Start" );
345 void ScHTMLLayoutParser::NextRow( ImportInfo* pInfo )
347 if ( bInCell )
348 CloseEntry( pInfo );
349 if ( nRowMax < ++nRowCnt )
350 nRowMax = nRowCnt;
351 nColCnt = nColCntStart;
352 nColOffset = nColOffsetStart;
353 bFirstRow = false;
356 bool ScHTMLLayoutParser::SeekOffset( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
357 SCCOL* pCol, sal_uInt16 nOffsetTol )
359 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
360 ScHTMLColOffset::const_iterator it = pOffset->find( nOffset );
361 bool bFound = it != pOffset->end();
362 sal_uInt16 nPos = it - pOffset->begin();
363 *pCol = static_cast<SCCOL>(nPos);
364 if ( bFound )
365 return true;
366 sal_uInt16 nCount = pOffset->size();
367 if ( !nCount )
368 return false;
369 // nPos is the position of insertion, that's where the next higher one is (or isn't)
370 if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
371 return true;
372 // Not smaller than everything else? Then compare with the next lower one
373 else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
375 (*pCol)--;
376 return true;
378 return false;
381 void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
382 sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
384 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
385 SCCOL nPos;
386 if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
387 nOffset = (sal_uInt16)(*pOffset)[nPos];
388 else
389 pOffset->insert( nOffset );
390 if ( nWidth )
392 if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
393 nWidth = (sal_uInt16)(*pOffset)[nPos] - nOffset;
394 else
395 pOffset->insert( nOffset + nWidth );
399 void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
400 sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
402 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
403 SCCOL nPos;
404 if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
405 nOffset = (sal_uInt16)(*pOffset)[nPos];
406 else
407 pOffset->insert( nOffset );
408 if ( nWidth )
410 if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
411 pOffset->insert( nOffset + nWidth );
415 void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
416 sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
418 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
419 SCCOL nPos;
420 if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
422 if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
423 nNewOffset = (sal_uInt16)(*pOffset)[nPos];
424 else
425 pOffset->insert( nNewOffset );
426 return ;
428 nOldOffset = (sal_uInt16)(*pOffset)[nPos];
429 SCCOL nPos2;
430 if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
432 nNewOffset = (sal_uInt16)(*pOffset)[nPos2];
433 return ;
435 long nDiff = nNewOffset - nOldOffset;
436 if ( nDiff < 0 )
440 const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
441 } while ( nPos-- );
443 else
447 const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
448 } while ( ++nPos < (sal_uInt16)pOffset->size() );
452 void ScHTMLLayoutParser::SkipLocked( ScEEParseEntry* pE, bool bJoin )
454 if ( ValidCol(pE->nCol) )
455 { // Or else this would create a wrong value at ScAddress (chance for an infinite loop)!
456 bool bBadCol = false;
457 bool bAgain;
458 ScRange aRange( pE->nCol, pE->nRow, 0,
459 pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
462 bAgain = false;
463 for ( size_t i = 0, nRanges = xLockedList->size(); i < nRanges; ++i )
465 ScRange* pR = (*xLockedList)[i];
466 if ( pR->Intersects( aRange ) )
468 pE->nCol = pR->aEnd.Col() + 1;
469 SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
470 if ( pE->nCol > MAXCOL || nTmp > MAXCOL )
471 bBadCol = true;
472 else
474 bAgain = true;
475 aRange.aStart.SetCol( pE->nCol );
476 aRange.aEnd.SetCol( nTmp );
478 break;
481 } while ( bAgain );
482 if ( bJoin && !bBadCol )
483 xLockedList->Join( aRange );
487 void ScHTMLLayoutParser::Adjust()
489 xLockedList->RemoveAll();
491 std::stack< ScHTMLAdjustStackEntry* > aStack;
492 ScHTMLAdjustStackEntry* pS = nullptr;
493 sal_uInt16 nTab = 0;
494 SCCOL nLastCol = SCCOL_MAX;
495 SCROW nNextRow = 0;
496 SCROW nCurRow = 0;
497 sal_uInt16 nPageWidth = (sal_uInt16) aPageSize.Width();
498 InnerMap* pTab = nullptr;
499 for (ScEEParseEntry* pE : maList)
501 if ( pE->nTab < nTab )
502 { // Table finished
503 if ( !aStack.empty() )
505 pS = aStack.top();
506 aStack.pop();
508 nLastCol = pS->nLastCol;
509 nNextRow = pS->nNextRow;
510 nCurRow = pS->nCurRow;
512 delete pS;
513 pS = nullptr;
514 nTab = pE->nTab;
515 if (pTables)
517 OuterMap::const_iterator it = pTables->find( nTab );
518 if ( it != pTables->end() )
519 pTab = it->second;
523 SCROW nRow = pE->nRow;
524 if ( pE->nCol <= nLastCol )
525 { // Next row
526 if ( pE->nRow < nNextRow )
527 pE->nRow = nCurRow = nNextRow;
528 else
529 nCurRow = nNextRow = pE->nRow;
530 SCROW nR = 0;
531 if ( pTab )
533 InnerMap::const_iterator it = pTab->find( nCurRow );
534 if ( it != pTab->end() )
535 nR = it->second;
537 if ( nR )
538 nNextRow += nR;
539 else
540 nNextRow++;
542 else
543 pE->nRow = nCurRow;
544 nLastCol = pE->nCol; // Read column
545 if ( pE->nTab > nTab )
546 { // New table
547 aStack.push( new ScHTMLAdjustStackEntry(
548 nLastCol, nNextRow, nCurRow ) );
549 nTab = pE->nTab;
550 if ( pTables )
552 OuterMap::const_iterator it = pTables->find( nTab );
553 if ( it != pTables->end() )
554 pTab = it->second;
556 // New line spacing
557 SCROW nR = 0;
558 if ( pTab )
560 InnerMap::const_iterator it = pTab->find( nCurRow );
561 if ( it != pTab->end() )
562 nR = it->second;
564 if ( nR )
565 nNextRow = nCurRow + nR;
566 else
567 nNextRow = nCurRow + 1;
569 if ( nTab == 0 )
570 pE->nWidth = nPageWidth;
571 else
572 { // Real table, no paragraphs on the field
573 if ( pTab )
575 SCROW nRowSpan = pE->nRowOverlap;
576 for ( SCROW j=0; j < nRowSpan; j++ )
577 { // RowSpan resulting from merged rows
578 SCROW nRows = 0;
579 InnerMap::const_iterator it = pTab->find( nRow+j );
580 if ( it != pTab->end() )
581 nRows = it->second;
582 if ( nRows > 1 )
584 pE->nRowOverlap += nRows - 1;
585 if ( j == 0 )
586 { // Merged rows move the next row
587 SCROW nTmp = nCurRow + nRows;
588 if ( nNextRow < nTmp )
589 nNextRow = nTmp;
595 // Real column
596 (void)SeekOffset( pColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
597 SCCOL nColBeforeSkip = pE->nCol;
598 SkipLocked( pE, false );
599 if ( pE->nCol != nColBeforeSkip )
601 SCCOL nCount = (SCCOL)pColOffset->size();
602 if ( nCount <= pE->nCol )
604 pE->nOffset = (sal_uInt16) (*pColOffset)[nCount-1];
605 MakeCol( pColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
607 else
609 pE->nOffset = (sal_uInt16) (*pColOffset)[pE->nCol];
612 SCCOL nPos;
613 if ( pE->nWidth && SeekOffset( pColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
614 pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
615 else
617 //FIXME: This may not be correct, but works anyway ...
618 pE->nColOverlap = 1;
620 xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
621 pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
622 // Take over MaxDimensions
623 SCCOL nColTmp = pE->nCol + pE->nColOverlap;
624 if ( nColMax < nColTmp )
625 nColMax = nColTmp;
626 SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
627 if ( nRowMax < nRowTmp )
628 nRowMax = nRowTmp;
630 while ( !aStack.empty() )
632 delete aStack.top();
633 aStack.pop();
637 sal_uInt16 ScHTMLLayoutParser::GetWidth( ScEEParseEntry* pE )
639 if ( pE->nWidth )
640 return pE->nWidth;
641 sal_Int32 nTmp = std::min( static_cast<sal_Int32>( pE->nCol -
642 nColCntStart + pE->nColOverlap),
643 static_cast<sal_Int32>( pLocalColOffset->size() - 1));
644 SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
645 sal_uInt16 nOff2 = (sal_uInt16) (*pLocalColOffset)[nPos];
646 if ( pE->nOffset < nOff2 )
647 return nOff2 - pE->nOffset;
648 return 0;
651 void ScHTMLLayoutParser::SetWidths()
653 ScEEParseEntry* pE;
654 SCCOL nCol;
655 if ( !nTableWidth )
656 nTableWidth = (sal_uInt16) aPageSize.Width();
657 SCCOL nColsPerRow = nMaxCol - nColCntStart;
658 if ( nColsPerRow <= 0 )
659 nColsPerRow = 1;
660 if ( pLocalColOffset->size() <= 2 )
661 { // Only PageSize, there was no width setting
662 sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
663 sal_uInt16 nOff = nColOffsetStart;
664 pLocalColOffset->clear();
665 for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
667 MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
669 nTableWidth = (sal_uInt16)(pLocalColOffset->back() - pLocalColOffset->front());
670 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
672 pE = maList[ i ];
673 if ( pE->nTab == nTable )
675 pE->nOffset = (sal_uInt16) (*pLocalColOffset)[pE->nCol - nColCntStart];
676 pE->nWidth = 0; // to be recalculated later
680 else
681 { // Some without width
682 // Why actually no pE?
683 if ( nFirstTableCell < maList.size() )
685 std::unique_ptr<sal_uInt16[]> pOffsets(new sal_uInt16[ nColsPerRow+1 ]);
686 memset( pOffsets.get(), 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
687 std::unique_ptr<sal_uInt16[]> pWidths(new sal_uInt16[ nColsPerRow ]);
688 memset( pWidths.get(), 0, nColsPerRow * sizeof(sal_uInt16) );
689 pOffsets[0] = nColOffsetStart;
690 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
692 pE = maList[ i ];
693 if ( pE->nTab == nTable && pE->nWidth )
695 nCol = pE->nCol - nColCntStart;
696 if ( nCol < nColsPerRow )
698 if ( pE->nColOverlap == 1 )
700 if ( pWidths[nCol] < pE->nWidth )
701 pWidths[nCol] = pE->nWidth;
703 else
704 { // try to find a single undefined width
705 sal_uInt16 nTotal = 0;
706 bool bFound = false;
707 SCCOL nHere = 0;
708 SCCOL nStop = std::min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
709 for ( ; nCol < nStop; nCol++ )
711 if ( pWidths[nCol] )
712 nTotal = nTotal + pWidths[nCol];
713 else
715 if ( bFound )
717 bFound = false;
718 break; // for
720 bFound = true;
721 nHere = nCol;
724 if ( bFound && pE->nWidth > nTotal )
725 pWidths[nHere] = pE->nWidth - nTotal;
730 sal_uInt16 nWidths = 0;
731 sal_uInt16 nUnknown = 0;
732 for ( nCol = 0; nCol < nColsPerRow; nCol++ )
734 if ( pWidths[nCol] )
735 nWidths = nWidths + pWidths[nCol];
736 else
737 nUnknown++;
739 if ( nUnknown )
741 sal_uInt16 nW = ((nWidths < nTableWidth) ?
742 ((nTableWidth - nWidths) / nUnknown) :
743 (nTableWidth / nUnknown));
744 for ( nCol = 0; nCol < nColsPerRow; nCol++ )
746 if ( !pWidths[nCol] )
747 pWidths[nCol] = nW;
750 for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
752 pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
754 pLocalColOffset->clear();
755 for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
757 MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
759 nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
761 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
763 pE = maList[ i ];
764 if ( pE->nTab == nTable )
766 nCol = pE->nCol - nColCntStart;
767 OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
768 if ( nCol < nColsPerRow )
770 pE->nOffset = pOffsets[nCol];
771 nCol = nCol + pE->nColOverlap;
772 if ( nCol > nColsPerRow )
773 nCol = nColsPerRow;
774 pE->nWidth = pOffsets[nCol] - pE->nOffset;
780 if ( !pLocalColOffset->empty() )
782 sal_uInt16 nMax = (sal_uInt16) pLocalColOffset->back();
783 if ( aPageSize.Width() < nMax )
784 aPageSize.Width() = nMax;
786 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
788 pE = maList[ i ];
789 if ( pE->nTab == nTable )
791 if ( !pE->nWidth )
793 pE->nWidth = GetWidth( pE );
794 OSL_ENSURE( pE->nWidth, "SetWidths: pE->nWidth == 0" );
796 MakeCol( pColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
801 void ScHTMLLayoutParser::Colonize( ScEEParseEntry* pE )
803 if ( pE->nCol == SCCOL_MAX )
804 pE->nCol = nColCnt;
805 if ( pE->nRow == SCROW_MAX )
806 pE->nRow = nRowCnt;
807 SCCOL nCol = pE->nCol;
808 SkipLocked( pE ); // Change of columns to the right
810 if ( nCol < pE->nCol )
811 { // Replaced
812 nCol = pE->nCol - nColCntStart;
813 SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size());
814 if ( nCol < nCount )
815 nColOffset = (sal_uInt16) (*pLocalColOffset)[nCol];
816 else
817 nColOffset = (sal_uInt16) (*pLocalColOffset)[nCount - 1];
819 pE->nOffset = nColOffset;
820 sal_uInt16 nWidth = GetWidth( pE );
821 MakeCol( pLocalColOffset, pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance );
822 if ( pE->nWidth )
823 pE->nWidth = nWidth;
824 nColOffset = pE->nOffset + nWidth;
825 if ( nTableWidth < nColOffset - nColOffsetStart )
826 nTableWidth = nColOffset - nColOffsetStart;
829 void ScHTMLLayoutParser::CloseEntry( ImportInfo* pInfo )
831 bInCell = false;
832 if ( bTabInTabCell )
833 { // From the stack in TableOff
834 bTabInTabCell = false;
835 bool found = false;
836 for (ScEEParseEntry* p : maList)
838 if ( pActEntry == p )
840 found = true;
841 break;
844 if ( !found )
845 delete pActEntry;
846 NewActEntry( maList.back() ); // New free flying pActEntry
847 return ;
849 if ( pActEntry->nTab == 0 )
850 pActEntry->nWidth = (sal_uInt16) aPageSize.Width();
851 Colonize( pActEntry );
852 nColCnt = pActEntry->nCol + pActEntry->nColOverlap;
853 if ( nMaxCol < nColCnt )
854 nMaxCol = nColCnt; // TableStack MaxCol
855 if ( nColMax < nColCnt )
856 nColMax = nColCnt; // Global MaxCol for ScEEParser GetDimensions!
857 EntryEnd( pActEntry, pInfo->aSelection );
858 ESelection& rSel = pActEntry->aSel;
859 while ( rSel.nStartPara < rSel.nEndPara
860 && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
861 { // Strip preceding empty paragraphs
862 rSel.nStartPara++;
864 while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
865 { // Strip successive empty paragraphs
866 rSel.nEndPara--;
867 rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
869 if ( rSel.nStartPara > rSel.nEndPara )
870 { // Gives GPF in CreateTextObject
871 OSL_FAIL( "CloseEntry: EditEngine ESelection Start > End" );
872 rSel.nEndPara = rSel.nStartPara;
874 if ( rSel.HasRange() )
875 pActEntry->aItemSet.Put( SfxBoolItem( ATTR_LINEBREAK, true ) );
876 maList.push_back( pActEntry );
877 NewActEntry( pActEntry ); // New free flying pActEntry
880 IMPL_LINK_TYPED( ScHTMLLayoutParser, HTMLImportHdl, ImportInfo&, rInfo, void )
882 switch ( rInfo.eState )
884 case HTMLIMP_NEXTTOKEN:
885 ProcToken( &rInfo );
886 break;
887 case HTMLIMP_UNKNOWNATTR:
888 ProcToken( &rInfo );
889 break;
890 case HTMLIMP_START:
891 break;
892 case HTMLIMP_END:
893 if ( rInfo.aSelection.nEndPos )
895 // If text remains: create paragraph, without calling CloseEntry().
896 if( bInCell ) // ...but only in opened table cells.
898 bInCell = false;
899 NextRow( &rInfo );
900 bInCell = true;
902 CloseEntry( &rInfo );
904 while ( nTableLevel > 0 )
905 TableOff( &rInfo ); // close tables, if </TABLE> missing
906 break;
907 case HTMLIMP_SETATTR:
908 break;
909 case HTMLIMP_INSERTTEXT:
910 break;
911 case HTMLIMP_INSERTPARA:
912 if ( nTableLevel < 1 )
914 CloseEntry( &rInfo );
915 NextRow( &rInfo );
917 break;
918 case HTMLIMP_INSERTFIELD:
919 break;
920 default:
921 OSL_FAIL("HTMLImportHdl: unknown ImportInfo.eState");
925 // Greatest common divisor (Euclid)
926 // Special case: 0 and something gives 1
927 static SCROW lcl_GGT( SCROW a, SCROW b )
929 if ( !a || !b )
930 return 1;
933 if ( a > b )
934 a -= SCROW(a / b) * b;
935 else
936 b -= SCROW(b / a) * a;
937 } while ( a && b );
938 return ((a != 0) ? a : b);
941 // Lowest common multiple: a * b / GCD(a,b)
942 static SCROW lcl_KGV( SCROW a, SCROW b )
944 if ( a > b ) // Make overflow even less likely
945 return (a / lcl_GGT(a,b)) * b;
946 else
947 return (b / lcl_GGT(a,b)) * a;
950 void ScHTMLLayoutParser::TableDataOn( ImportInfo* pInfo )
952 if ( bInCell )
953 CloseEntry( pInfo );
954 if ( !nTableLevel )
956 OSL_FAIL( "dumbo doc! <TH> or <TD> without previous <TABLE>" );
957 TableOn( pInfo );
959 bInCell = true;
960 bool bHorJustifyCenterTH = (pInfo->nToken == HTML_TABLEHEADER_ON);
961 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
962 for (const auto & rOption : rOptions)
964 switch( rOption.GetToken() )
966 case HTML_O_COLSPAN:
968 pActEntry->nColOverlap = ( SCCOL ) rOption.GetString().toInt32();
970 break;
971 case HTML_O_ROWSPAN:
973 pActEntry->nRowOverlap = ( SCROW ) rOption.GetString().toInt32();
975 break;
976 case HTML_O_ALIGN:
978 bHorJustifyCenterTH = false;
979 SvxCellHorJustify eVal;
980 const OUString& rOptVal = rOption.GetString();
981 if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
982 eVal = SVX_HOR_JUSTIFY_RIGHT;
983 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
984 eVal = SVX_HOR_JUSTIFY_CENTER;
985 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
986 eVal = SVX_HOR_JUSTIFY_LEFT;
987 else
988 eVal = SVX_HOR_JUSTIFY_STANDARD;
989 if ( eVal != SVX_HOR_JUSTIFY_STANDARD )
990 pActEntry->aItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY) );
992 break;
993 case HTML_O_VALIGN:
995 SvxCellVerJustify eVal;
996 const OUString& rOptVal = rOption.GetString();
997 if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
998 eVal = SVX_VER_JUSTIFY_TOP;
999 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
1000 eVal = SVX_VER_JUSTIFY_CENTER;
1001 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
1002 eVal = SVX_VER_JUSTIFY_BOTTOM;
1003 else
1004 eVal = SVX_VER_JUSTIFY_STANDARD;
1005 pActEntry->aItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY) );
1007 break;
1008 case HTML_O_WIDTH:
1010 pActEntry->nWidth = GetWidthPixel( rOption );
1012 break;
1013 case HTML_O_BGCOLOR:
1015 Color aColor;
1016 rOption.GetColor( aColor );
1017 pActEntry->aItemSet.Put(
1018 SvxBrushItem( aColor, ATTR_BACKGROUND ) );
1020 break;
1021 case HTML_O_SDVAL:
1023 pActEntry->pValStr = new OUString( rOption.GetString() );
1025 break;
1026 case HTML_O_SDNUM:
1028 pActEntry->pNumStr = new OUString( rOption.GetString() );
1030 break;
1033 pActEntry->nCol = nColCnt;
1034 pActEntry->nRow = nRowCnt;
1035 pActEntry->nTab = nTable;
1037 if ( bHorJustifyCenterTH )
1038 pActEntry->aItemSet.Put(
1039 SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY) );
1042 void ScHTMLLayoutParser::TableRowOn( ImportInfo* pInfo )
1044 if ( nColCnt > nColCntStart )
1045 NextRow( pInfo ); // The optional TableRowOff wasn't there
1046 nColOffset = nColOffsetStart;
1049 void ScHTMLLayoutParser::TableRowOff( ImportInfo* pInfo )
1051 NextRow( pInfo );
1054 void ScHTMLLayoutParser::TableDataOff( ImportInfo* pInfo )
1056 if ( bInCell )
1057 CloseEntry( pInfo ); // Only if it really was one
1060 void ScHTMLLayoutParser::TableOn( ImportInfo* pInfo )
1062 OUString aTabName;
1064 if ( ++nTableLevel > 1 )
1065 { // Table in Table
1066 sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize()
1067 Colonize( pActEntry );
1068 aTableStack.push( new ScHTMLTableStackEntry(
1069 pActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
1070 nRowCnt, nColCntStart, nMaxCol, nTable,
1071 nTableWidth, nColOffset, nColOffsetStart,
1072 bFirstRow ) );
1073 sal_uInt16 nLastWidth = nTableWidth;
1074 nTableWidth = GetWidth( pActEntry );
1075 if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
1076 { // There must be more than one, so this one cannot be enough
1077 nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
1079 nLastWidth = nTableWidth;
1080 if ( pInfo->nToken == HTML_TABLE_ON )
1081 { // It can still be TD or TH, if we didn't have a TABLE earlier
1082 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1083 for (const auto & rOption : rOptions)
1085 switch( rOption.GetToken() )
1087 case HTML_O_WIDTH:
1088 { // Percent: of document width or outer cell
1089 nTableWidth = GetWidthPixel( rOption );
1091 break;
1092 case HTML_O_BORDER:
1093 // Border is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1094 break;
1095 case HTML_O_ID:
1096 aTabName = rOption.GetString();
1097 break;
1101 bInCell = false;
1102 if ( bTabInTabCell && !(nTableWidth < nLastWidth) )
1103 { // Multiple tables in one cell, underneath each other
1104 bTabInTabCell = false;
1105 NextRow( pInfo );
1107 else
1108 { // It start's in this cell or next to each other
1109 bTabInTabCell = false;
1110 nColCntStart = nColCnt;
1111 nColOffset = nTmpColOffset;
1112 nColOffsetStart = nColOffset;
1115 ScEEParseEntry* pE = nullptr;
1116 if (maList.size())
1117 pE = maList.back();
1118 NewActEntry( pE ); // New free flying pActEntry
1119 xLockedList = new ScRangeList;
1121 else
1122 { // Simple table at the document level
1123 EntryEnd( pActEntry, pInfo->aSelection );
1124 if ( pActEntry->aSel.HasRange() )
1125 { // Flying text left
1126 CloseEntry( pInfo );
1127 NextRow( pInfo );
1129 aTableStack.push( new ScHTMLTableStackEntry(
1130 pActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
1131 nRowCnt, nColCntStart, nMaxCol, nTable,
1132 nTableWidth, nColOffset, nColOffsetStart,
1133 bFirstRow ) );
1134 // As soon as we have multiple tables we need to be tolerant with the offsets.
1135 if (nMaxTable > 0)
1136 nOffsetTolerance = SC_HTML_OFFSET_TOLERANCE_LARGE;
1137 nTableWidth = 0;
1138 if ( pInfo->nToken == HTML_TABLE_ON )
1140 // It can still be TD or TH, if we didn't have a TABLE earlier
1141 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1142 for (const auto & rOption : rOptions)
1144 switch( rOption.GetToken() )
1146 case HTML_O_WIDTH:
1147 { // Percent: of document width or outer cell
1148 nTableWidth = GetWidthPixel( rOption );
1150 break;
1151 case HTML_O_BORDER:
1152 //BorderOn is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1153 break;
1154 case HTML_O_ID:
1155 aTabName = rOption.GetString();
1156 break;
1161 nTable = ++nMaxTable;
1162 bFirstRow = true;
1163 nFirstTableCell = maList.size();
1165 pLocalColOffset = new ScHTMLColOffset;
1166 MakeColNoRef( pLocalColOffset, nColOffsetStart, 0, 0, 0 );
1169 void ScHTMLLayoutParser::TableOff( ImportInfo* pInfo )
1171 if ( bInCell )
1172 CloseEntry( pInfo );
1173 if ( nColCnt > nColCntStart )
1174 TableRowOff( pInfo ); // The optional TableRowOff wasn't
1175 if ( !nTableLevel )
1177 OSL_FAIL( "dumbo doc! </TABLE> without opening <TABLE>" );
1178 return ;
1180 if ( --nTableLevel > 0 )
1181 { // Table in Table done
1182 if ( !aTableStack.empty() )
1184 ScHTMLTableStackEntry* pS = aTableStack.top();
1185 aTableStack.pop();
1187 ScEEParseEntry* pE = pS->pCellEntry;
1188 SCROW nRows = nRowCnt - pS->nRowCnt;
1189 if ( nRows > 1 )
1190 { // Insert size of table at this position
1191 SCROW nRow = pS->nRowCnt;
1192 sal_uInt16 nTab = pS->nTable;
1193 if ( !pTables )
1194 pTables = new OuterMap;
1195 // Height of outer table
1196 OuterMap::const_iterator it = pTables->find( nTab );
1197 InnerMap* pTab1;
1198 if ( it == pTables->end() )
1200 pTab1 = new InnerMap;
1201 (*pTables)[ nTab ] = pTab1;
1203 else
1204 pTab1 = it->second;
1205 SCROW nRowSpan = pE->nRowOverlap;
1206 SCROW nRowKGV;
1207 SCROW nRowsPerRow1; // Outer table
1208 SCROW nRowsPerRow2; // Inner table
1209 if ( nRowSpan > 1 )
1210 { // LCM to which we can map the inner and outer rows
1211 nRowKGV = lcl_KGV( nRowSpan, nRows );
1212 nRowsPerRow1 = nRowKGV / nRowSpan;
1213 nRowsPerRow2 = nRowKGV / nRows;
1215 else
1217 nRowKGV = nRowsPerRow1 = nRows;
1218 nRowsPerRow2 = 1;
1220 InnerMap* pTab2 = nullptr;
1221 if ( nRowsPerRow2 > 1 )
1222 { // Height of the inner table
1223 pTab2 = new InnerMap;
1224 (*pTables)[ nTable ] = pTab2;
1226 // Abuse void* Data entry of the Table class for height mapping
1227 if ( nRowKGV > 1 )
1229 if ( nRowsPerRow1 > 1 )
1230 { // Outer
1231 for ( SCROW j=0; j < nRowSpan; j++ )
1233 sal_uLong nRowKey = nRow + j;
1234 SCROW nR = (*pTab1)[ nRowKey ];
1235 if ( !nR )
1236 (*pTab1)[ nRowKey ] = nRowsPerRow1;
1237 else if ( nRowsPerRow1 > nR )
1238 (*pTab1)[ nRowKey ] = nRowsPerRow1;
1239 //TODO: How can we improve on this?
1240 else if ( nRowsPerRow1 < nR && nRowSpan == 1
1241 && nTable == nMaxTable )
1242 { // Still some space left, merge in a better way (if possible)
1243 SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
1244 nR += nAdd;
1245 if ( (nR % nRows) == 0 )
1246 { // Only if representable
1247 SCROW nR2 = (*pTab1)[ nRowKey+1 ];
1248 if ( nR2 > nAdd )
1249 { // Only if we really have enough space
1250 (*pTab1)[ nRowKey ] = nR;
1251 (*pTab1)[ nRowKey+1 ] = nR2 - nAdd;
1252 nRowsPerRow2 = nR / nRows;
1258 if ( nRowsPerRow2 > 1 )
1259 { // Inner
1260 if ( !pTab2 )
1261 { // nRowsPerRow2 could be've been incremented
1262 pTab2 = new InnerMap;
1263 (*pTables)[ nTable ] = pTab2;
1265 for ( SCROW j=0; j < nRows; j++ )
1267 sal_uLong nRowKey = nRow + j;
1268 (*pTab2)[ nRowKey ] = nRowsPerRow2;
1274 SetWidths();
1276 if ( !pE->nWidth )
1277 pE->nWidth = nTableWidth;
1278 else if ( pE->nWidth < nTableWidth )
1280 sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
1281 sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
1282 ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
1283 sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
1284 pE->nWidth = nNewOffset - pE->nOffset;
1285 pS->nTableWidth = pS->nTableWidth + nTmp;
1286 if ( pS->nColOffset >= nOldOffset )
1287 pS->nColOffset = pS->nColOffset + nTmp;
1290 nColCnt = pE->nCol + pE->nColOverlap;
1291 nRowCnt = pS->nRowCnt;
1292 nColCntStart = pS->nColCntStart;
1293 nMaxCol = pS->nMaxCol;
1294 nTable = pS->nTable;
1295 nTableWidth = pS->nTableWidth;
1296 nFirstTableCell = pS->nFirstTableCell;
1297 nColOffset = pS->nColOffset;
1298 nColOffsetStart = pS->nColOffsetStart;
1299 bFirstRow = pS->bFirstRow;
1300 xLockedList = pS->xLockedList;
1301 delete pLocalColOffset;
1302 pLocalColOffset = pS->pLocalColOffset;
1303 delete pActEntry;
1304 // pActEntry is kept around if a table is started in the same row
1305 // (anything's possible in HTML); will be deleted by CloseEntry
1306 pActEntry = pE;
1307 delete pS;
1309 bTabInTabCell = true;
1310 bInCell = true;
1312 else
1313 { // Simple table finished
1314 SetWidths();
1315 nMaxCol = 0;
1316 nTable = 0;
1317 if ( !aTableStack.empty() )
1319 ScHTMLTableStackEntry* pS = aTableStack.top();
1320 aTableStack.pop();
1321 delete pLocalColOffset;
1322 pLocalColOffset = pS->pLocalColOffset;
1323 delete pS;
1328 void ScHTMLLayoutParser::Image( ImportInfo* pInfo )
1330 pActEntry->maImageList.push_back( o3tl::make_unique<ScHTMLImage>() );
1331 ScHTMLImage* pImage = pActEntry->maImageList.back().get();
1332 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1333 for (const auto & rOption : rOptions)
1335 switch( rOption.GetToken() )
1337 case HTML_O_SRC:
1339 pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, rOption.GetString() );
1341 break;
1342 case HTML_O_ALT:
1344 if ( !pActEntry->bHasGraphic )
1345 { // ALT text only if not any image loaded
1346 if (!pActEntry->aAltText.isEmpty())
1347 pActEntry->aAltText += "; ";
1349 pActEntry->aAltText += rOption.GetString();
1352 break;
1353 case HTML_O_WIDTH:
1355 pImage->aSize.Width() = (long)rOption.GetNumber();
1357 break;
1358 case HTML_O_HEIGHT:
1360 pImage->aSize.Height() = (long)rOption.GetNumber();
1362 break;
1363 case HTML_O_HSPACE:
1365 pImage->aSpace.X() = (long)rOption.GetNumber();
1367 break;
1368 case HTML_O_VSPACE:
1370 pImage->aSpace.Y() = (long)rOption.GetNumber();
1372 break;
1375 if (pImage->aURL.isEmpty())
1377 OSL_FAIL( "Image: graphic without URL ?!?" );
1378 return ;
1381 sal_uInt16 nFormat;
1382 Graphic* pGraphic = new Graphic;
1383 GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
1384 if ( GRFILTER_OK != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
1385 *pGraphic, &rFilter, &nFormat ) )
1387 delete pGraphic;
1388 return ; // Bad luck
1390 if ( !pActEntry->bHasGraphic )
1391 { // discard any ALT text in this cell if we have any image
1392 pActEntry->bHasGraphic = true;
1393 (pActEntry->aAltText).clear();
1395 pImage->aFilterName = rFilter.GetImportFormatName( nFormat );
1396 pImage->pGraphic = pGraphic;
1397 if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
1399 OutputDevice* pDefaultDev = Application::GetDefaultDevice();
1400 pImage->aSize = pDefaultDev->LogicToPixel( pGraphic->GetPrefSize(),
1401 pGraphic->GetPrefMapMode() );
1403 if ( pActEntry->maImageList.size() > 0 )
1405 long nWidth = 0;
1406 for (std::unique_ptr<ScHTMLImage> & pI : pActEntry->maImageList)
1408 if ( pI->nDir & nHorizontal )
1409 nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
1410 else
1411 nWidth = 0;
1413 if ( pActEntry->nWidth
1414 && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
1415 >= pActEntry->nWidth) )
1416 pActEntry->maImageList.back()->nDir = nVertical;
1420 void ScHTMLLayoutParser::ColOn( ImportInfo* pInfo )
1422 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1423 for (const auto & rOption : rOptions)
1425 switch( rOption.GetToken() )
1427 case HTML_O_WIDTH:
1429 sal_uInt16 nVal = GetWidthPixel( rOption );
1430 MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
1431 nColOffset = nColOffset + nVal;
1433 break;
1438 sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption& rOption )
1440 const OUString& rOptVal = rOption.GetString();
1441 if ( rOptVal.indexOf('%') != -1 )
1442 { // Percent
1443 sal_uInt16 nW = (nTableWidth ? nTableWidth : (sal_uInt16) aPageSize.Width());
1444 return (sal_uInt16)((rOption.GetNumber() * nW) / 100);
1446 else
1448 if ( rOptVal.indexOf('*') != -1 )
1449 { // Relative to what?
1450 // TODO: Collect all relative values in ColArray and then MakeCol
1451 return 0;
1453 else
1454 return (sal_uInt16)rOption.GetNumber(); // Pixel
1458 void ScHTMLLayoutParser::AnchorOn( ImportInfo* pInfo )
1460 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1461 for (const auto & rOption : rOptions)
1463 switch( rOption.GetToken() )
1465 case HTML_O_NAME:
1467 pActEntry->pName = new OUString(rOption.GetString());
1469 break;
1474 bool ScHTMLLayoutParser::IsAtBeginningOfText( ImportInfo* pInfo )
1476 ESelection& rSel = pActEntry->aSel;
1477 return rSel.nStartPara == rSel.nEndPara &&
1478 rSel.nStartPara <= pInfo->aSelection.nEndPara &&
1479 pEdit->GetTextLen( rSel.nStartPara ) == 0;
1482 void ScHTMLLayoutParser::FontOn( ImportInfo* pInfo )
1484 if ( IsAtBeginningOfText( pInfo ) )
1485 { // Only at the start of the text; applies to whole line
1486 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1487 for (const auto & rOption : rOptions)
1489 switch( rOption.GetToken() )
1491 case HTML_O_FACE :
1493 const OUString& rFace = rOption.GetString();
1494 OUString aFontName;
1495 sal_Int32 nPos = 0;
1496 while( nPos != -1 )
1498 // Font list, VCL uses the semicolon as separator
1499 // HTML uses the comma
1500 OUString aFName = rFace.getToken( 0, ',', nPos );
1501 aFName = comphelper::string::strip(aFName, ' ');
1502 if( !aFontName.isEmpty() )
1503 aFontName += ";";
1504 aFontName += aFName;
1506 if ( !aFontName.isEmpty() )
1507 pActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
1508 aFontName, EMPTY_OUSTRING, PITCH_DONTKNOW,
1509 RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
1511 break;
1512 case HTML_O_SIZE :
1514 sal_uInt16 nSize = (sal_uInt16) rOption.GetNumber();
1515 if ( nSize == 0 )
1516 nSize = 1;
1517 else if ( nSize > SC_HTML_FONTSIZES )
1518 nSize = SC_HTML_FONTSIZES;
1519 pActEntry->aItemSet.Put( SvxFontHeightItem(
1520 maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
1522 break;
1523 case HTML_O_COLOR :
1525 Color aColor;
1526 rOption.GetColor( aColor );
1527 pActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
1529 break;
1535 void ScHTMLLayoutParser::ProcToken( ImportInfo* pInfo )
1537 bool bSetLastToken = true;
1538 switch ( pInfo->nToken )
1540 case HTML_META:
1542 HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser);
1543 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1544 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
1545 pParser->ParseMetaOptions(
1546 xDPS->getDocumentProperties(),
1547 mpDoc->GetDocumentShell()->GetHeaderAttributes() );
1549 break;
1550 case HTML_TITLE_ON:
1552 bInTitle = true;
1553 aString.clear();
1555 break;
1556 case HTML_TITLE_OFF:
1558 if ( bInTitle && !aString.isEmpty() )
1560 // Remove blanks from line brakes
1561 aString = aString.trim();
1562 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1563 mpDoc->GetDocumentShell()->GetModel(),
1564 uno::UNO_QUERY_THROW);
1565 xDPS->getDocumentProperties()->setTitle(aString);
1567 bInTitle = false;
1569 break;
1570 case HTML_TABLE_ON:
1572 TableOn( pInfo );
1574 break;
1575 case HTML_COL_ON:
1577 ColOn( pInfo );
1579 break;
1580 case HTML_TABLEHEADER_ON: // Opens row
1582 if ( bInCell )
1583 CloseEntry( pInfo );
1584 // Do not set bInCell to true, TableDataOn does that
1585 pActEntry->aItemSet.Put(
1586 SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT) );
1587 SAL_FALLTHROUGH;
1589 case HTML_TABLEDATA_ON: // Opens cell
1591 TableDataOn( pInfo );
1593 break;
1594 case HTML_TABLEHEADER_OFF:
1595 case HTML_TABLEDATA_OFF: // Closes cell
1597 TableDataOff( pInfo );
1599 break;
1600 case HTML_TABLEROW_ON: // Before first cell in row
1602 TableRowOn( pInfo );
1604 break;
1605 case HTML_TABLEROW_OFF: // After last cell in row
1607 TableRowOff( pInfo );
1609 break;
1610 case HTML_TABLE_OFF:
1612 TableOff( pInfo );
1614 break;
1615 case HTML_IMAGE:
1617 Image( pInfo );
1619 break;
1620 case HTML_PARABREAK_OFF:
1621 { // We continue vertically after an image
1622 if ( pActEntry->maImageList.size() > 0 )
1623 pActEntry->maImageList.back()->nDir = nVertical;
1625 break;
1626 case HTML_ANCHOR_ON:
1628 AnchorOn( pInfo );
1630 break;
1631 case HTML_FONT_ON :
1633 FontOn( pInfo );
1635 break;
1636 case HTML_BIGPRINT_ON :
1638 // TODO: Remember current font size and increase by 1
1639 if ( IsAtBeginningOfText( pInfo ) )
1640 pActEntry->aItemSet.Put( SvxFontHeightItem(
1641 maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
1643 break;
1644 case HTML_SMALLPRINT_ON :
1646 // TODO: Remember current font size and decrease by 1
1647 if ( IsAtBeginningOfText( pInfo ) )
1648 pActEntry->aItemSet.Put( SvxFontHeightItem(
1649 maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
1651 break;
1652 case HTML_BOLD_ON :
1653 case HTML_STRONG_ON :
1655 if ( IsAtBeginningOfText( pInfo ) )
1656 pActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1657 ATTR_FONT_WEIGHT ) );
1659 break;
1660 case HTML_ITALIC_ON :
1661 case HTML_EMPHASIS_ON :
1662 case HTML_ADDRESS_ON :
1663 case HTML_BLOCKQUOTE_ON :
1664 case HTML_BLOCKQUOTE30_ON :
1665 case HTML_CITIATION_ON :
1666 case HTML_VARIABLE_ON :
1668 if ( IsAtBeginningOfText( pInfo ) )
1669 pActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1670 ATTR_FONT_POSTURE ) );
1672 break;
1673 case HTML_DEFINSTANCE_ON :
1675 if ( IsAtBeginningOfText( pInfo ) )
1677 pActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1678 ATTR_FONT_WEIGHT ) );
1679 pActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1680 ATTR_FONT_POSTURE ) );
1683 break;
1684 case HTML_UNDERLINE_ON :
1686 if ( IsAtBeginningOfText( pInfo ) )
1687 pActEntry->aItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE,
1688 ATTR_FONT_UNDERLINE ) );
1690 break;
1691 case HTML_TEXTTOKEN:
1693 if ( bInTitle )
1694 aString += pInfo->aText;
1696 break;
1697 default:
1698 { // Don't set nLastToken!
1699 bSetLastToken = false;
1702 if ( bSetLastToken )
1703 nLastToken = pInfo->nToken;
1706 // HTML DATA QUERY PARSER
1708 template< typename Type >
1709 inline Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
1710 { return std::max( std::min( rValue, rMax ), rMin ); }
1712 ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
1713 ScEEParseEntry( rItemSet ),
1714 mbImportAlways( false )
1716 nTab = nTableId;
1717 bEntirePara = false;
1720 bool ScHTMLEntry::HasContents() const
1722 return mbImportAlways || aSel.HasRange() || !aAltText.isEmpty() || IsTable();
1725 void ScHTMLEntry::AdjustStart( const ImportInfo& rInfo )
1727 // set start position
1728 aSel.nStartPara = rInfo.aSelection.nStartPara;
1729 aSel.nStartPos = rInfo.aSelection.nStartPos;
1730 // adjust end position
1731 if( (aSel.nEndPara < aSel.nStartPara) || ((aSel.nEndPara == aSel.nStartPara) && (aSel.nEndPos < aSel.nStartPos)) )
1733 aSel.nEndPara = aSel.nStartPara;
1734 aSel.nEndPos = aSel.nStartPos;
1738 void ScHTMLEntry::AdjustEnd( const ImportInfo& rInfo )
1740 OSL_ENSURE( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
1741 ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
1742 "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
1743 // set end position
1744 aSel.nEndPara = rInfo.aSelection.nEndPara;
1745 aSel.nEndPos = rInfo.aSelection.nEndPos;
1748 void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
1750 // strip leading empty paragraphs
1751 while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
1753 ++aSel.nStartPara;
1754 aSel.nStartPos = 0;
1756 // strip trailing empty paragraphs
1757 while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
1759 --aSel.nEndPara;
1760 aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
1764 /** A map of ScHTMLTable objects.
1766 Organizes the tables with a unique table key. Stores nested tables inside
1767 the parent table and forms in this way a tree structure of tables. An
1768 instance of this class ownes the contained table objects and deletes them
1769 on destruction.
1771 class ScHTMLTableMap
1773 private:
1774 typedef std::shared_ptr< ScHTMLTable > ScHTMLTablePtr;
1775 typedef std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
1777 public:
1778 typedef ScHTMLTableStdMap::iterator iterator;
1779 typedef ScHTMLTableStdMap::const_iterator const_iterator;
1781 private:
1782 ScHTMLTable& mrParentTable; /// Reference to parent table.
1783 ScHTMLTableStdMap maTables; /// Container for all table objects.
1784 mutable ScHTMLTable* mpCurrTable; /// Current table, used for fast search.
1786 public:
1787 explicit ScHTMLTableMap( ScHTMLTable& rParentTable );
1788 virtual ~ScHTMLTableMap();
1790 inline const_iterator begin() const { return maTables.begin(); }
1791 inline const_iterator end() const { return maTables.end(); }
1793 /** Returns the specified table.
1794 @param nTableId Unique identifier of the table.
1795 @param bDeep true = searches deep in all nested table; false = only in this container. */
1796 ScHTMLTable* FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
1798 /** Inserts a new table into the container. This container owns the created table.
1799 @param bPreFormText true = New table is based on preformatted text (<pre> tag). */
1800 ScHTMLTable* CreateTable( const ImportInfo& rInfo, bool bPreFormText );
1802 private:
1803 /** Sets a working table with its index for search optimization. */
1804 inline void SetCurrTable( ScHTMLTable* pTable ) const
1805 { if( pTable ) mpCurrTable = pTable; }
1808 ScHTMLTableMap::ScHTMLTableMap( ScHTMLTable& rParentTable ) :
1809 mrParentTable(rParentTable),
1810 mpCurrTable(nullptr)
1814 ScHTMLTableMap::~ScHTMLTableMap()
1818 ScHTMLTable* ScHTMLTableMap::FindTable( ScHTMLTableId nTableId, bool bDeep ) const
1820 ScHTMLTable* pResult = nullptr;
1821 if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
1822 pResult = mpCurrTable; // cached table
1823 else
1825 const_iterator aFind = maTables.find( nTableId );
1826 if( aFind != maTables.end() )
1827 pResult = aFind->second.get(); // table from this container
1830 // not found -> search deep in nested tables
1831 if( !pResult && bDeep )
1832 for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
1833 pResult = aIter->second->FindNestedTable( nTableId );
1835 SetCurrTable( pResult );
1836 return pResult;
1839 ScHTMLTable* ScHTMLTableMap::CreateTable( const ImportInfo& rInfo, bool bPreFormText )
1841 ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText );
1842 maTables[ pTable->GetTableId() ].reset( pTable );
1843 SetCurrTable( pTable );
1844 return pTable;
1847 /** Simplified forward iterator for convenience.
1849 Before the iterator can be dereferenced, it must be tested with the is()
1850 method. The iterator may be invalid directly after construction (e.g. empty
1851 container).
1853 class ScHTMLTableIterator
1855 public:
1856 /** Constructs the iterator for the passed table map.
1857 @param pTableMap Pointer to the table map (is allowed to be NULL). */
1858 explicit ScHTMLTableIterator( const ScHTMLTableMap* pTableMap );
1860 inline bool is() const { return mpTableMap && maIter != maEnd; }
1861 inline ScHTMLTable* operator->() { return maIter->second.get(); }
1862 inline ScHTMLTableIterator& operator++() { ++maIter; return *this; }
1864 private:
1865 ScHTMLTableMap::const_iterator maIter;
1866 ScHTMLTableMap::const_iterator maEnd;
1867 const ScHTMLTableMap* mpTableMap;
1870 ScHTMLTableIterator::ScHTMLTableIterator( const ScHTMLTableMap* pTableMap ) :
1871 mpTableMap(pTableMap)
1873 if( pTableMap )
1875 maIter = pTableMap->begin();
1876 maEnd = pTableMap->end();
1880 ScHTMLTableAutoId::ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId ) :
1881 mnTableId( rnUnusedId ),
1882 mrnUnusedId( rnUnusedId )
1884 ++mrnUnusedId;
1887 ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const ImportInfo& rInfo, bool bPreFormText ) :
1888 mpParentTable( &rParentTable ),
1889 maTableId( rParentTable.maTableId.mrnUnusedId ),
1890 maTableItemSet( rParentTable.GetCurrItemSet() ),
1891 mrEditEngine( rParentTable.mrEditEngine ),
1892 mrEEParseList( rParentTable.mrEEParseList ),
1893 mpCurrEntryList( nullptr ),
1894 maSize( 1, 1 ),
1895 mpParser(rParentTable.mpParser),
1896 mbBorderOn( false ),
1897 mbPreFormText( bPreFormText ),
1898 mbRowOn( false ),
1899 mbDataOn( false ),
1900 mbPushEmptyLine( false )
1902 if( mbPreFormText )
1904 ImplRowOn();
1905 ImplDataOn( ScHTMLSize( 1, 1 ) );
1907 else
1909 ProcessFormatOptions( maTableItemSet, rInfo );
1910 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
1911 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
1912 for (; itr != itrEnd; ++itr)
1914 switch( itr->GetToken() )
1916 case HTML_O_BORDER:
1917 mbBorderOn = itr->GetString().isEmpty() || (itr->GetNumber() != 0);
1918 break;
1919 case HTML_O_ID:
1920 maTableName = itr->GetString();
1921 break;
1926 CreateNewEntry( rInfo );
1929 ScHTMLTable::ScHTMLTable(
1930 SfxItemPool& rPool,
1931 EditEngine& rEditEngine,
1932 std::vector< ScEEParseEntry* >& rEEParseList,
1933 ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser
1935 mpParentTable( nullptr ),
1936 maTableId( rnUnusedId ),
1937 maTableItemSet( rPool ),
1938 mrEditEngine( rEditEngine ),
1939 mrEEParseList( rEEParseList ),
1940 mpCurrEntryList( nullptr ),
1941 maSize( 1, 1 ),
1942 mpParser(pParser),
1943 mbBorderOn( false ),
1944 mbPreFormText( false ),
1945 mbRowOn( false ),
1946 mbDataOn( false ),
1947 mbPushEmptyLine( false )
1949 // open the first "cell" of the document
1950 ImplRowOn();
1951 ImplDataOn( ScHTMLSize( 1, 1 ) );
1952 mxCurrEntry = CreateEntry();
1955 ScHTMLTable::~ScHTMLTable()
1959 const SfxItemSet& ScHTMLTable::GetCurrItemSet() const
1961 // first try cell item set, then row item set, then table item set
1962 return mxDataItemSet.get() ? *mxDataItemSet : (mxRowItemSet.get() ? *mxRowItemSet : maTableItemSet);
1965 ScHTMLSize ScHTMLTable::GetSpan( const ScHTMLPos& rCellPos ) const
1967 ScHTMLSize aSpan( 1, 1 );
1968 const ScRange* pRange = nullptr;
1969 if( ( (pRange = maVMergedCells.Find( rCellPos.MakeAddr() ) ) != nullptr)
1970 || ( (pRange = maHMergedCells.Find( rCellPos.MakeAddr() ) ) != nullptr)
1972 aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
1973 return aSpan;
1976 ScHTMLTable* ScHTMLTable::FindNestedTable( ScHTMLTableId nTableId ) const
1978 return mxNestedTables.get() ? mxNestedTables->FindTable( nTableId ) : nullptr;
1981 void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
1983 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::PutItem - no current entry" );
1984 if( mxCurrEntry.get() && mxCurrEntry->IsEmpty() )
1985 mxCurrEntry->GetItemSet().Put( rItem );
1988 void ScHTMLTable::PutText( const ImportInfo& rInfo )
1990 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::PutText - no current entry" );
1991 if( mxCurrEntry.get() )
1993 if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
1994 mxCurrEntry->AdjustStart( rInfo );
1995 else
1996 mxCurrEntry->AdjustEnd( rInfo );
2000 void ScHTMLTable::InsertPara( const ImportInfo& rInfo )
2002 if( mxCurrEntry.get() && mbDataOn && !IsEmptyCell() )
2003 mxCurrEntry->SetImportAlways();
2004 PushEntry( rInfo );
2005 CreateNewEntry( rInfo );
2006 InsertLeadingEmptyLine();
2009 void ScHTMLTable::BreakOn()
2011 // empty line, if <br> is at start of cell
2012 mbPushEmptyLine = !mbPreFormText && mbDataOn && IsEmptyCell();
2015 void ScHTMLTable::HeadingOn()
2017 // call directly, InsertPara() has not been called before
2018 InsertLeadingEmptyLine();
2021 void ScHTMLTable::InsertLeadingEmptyLine()
2023 // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
2024 mbPushEmptyLine = !mbPreFormText && mbDataOn && !IsEmptyCell();
2027 void ScHTMLTable::AnchorOn()
2029 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::AnchorOn - no current entry" );
2030 // don't skip entries with single hyperlinks
2031 if( mxCurrEntry.get() )
2032 mxCurrEntry->SetImportAlways();
2035 ScHTMLTable* ScHTMLTable::TableOn( const ImportInfo& rInfo )
2037 PushEntry( rInfo );
2038 return InsertNestedTable( rInfo, false );
2041 ScHTMLTable* ScHTMLTable::TableOff( const ImportInfo& rInfo )
2043 return mbPreFormText ? this : CloseTable( rInfo );
2046 ScHTMLTable* ScHTMLTable::PreOn( const ImportInfo& rInfo )
2048 PushEntry( rInfo );
2049 return InsertNestedTable( rInfo, true );
2052 ScHTMLTable* ScHTMLTable::PreOff( const ImportInfo& rInfo )
2054 return mbPreFormText ? CloseTable( rInfo ) : this;
2057 void ScHTMLTable::RowOn( const ImportInfo& rInfo )
2059 PushEntry( rInfo, true );
2060 if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
2062 ImplRowOn();
2063 ProcessFormatOptions( *mxRowItemSet, rInfo );
2065 CreateNewEntry( rInfo );
2068 void ScHTMLTable::RowOff( const ImportInfo& rInfo )
2070 PushEntry( rInfo, true );
2071 if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
2072 ImplRowOff();
2073 CreateNewEntry( rInfo );
2076 namespace {
2079 * Decode a number format string stored in Excel-generated HTML's CSS
2080 * region.
2082 OUString decodeNumberFormat(const OUString& rFmt)
2084 OUStringBuffer aBuf;
2085 const sal_Unicode* p = rFmt.getStr();
2086 sal_Int32 n = rFmt.getLength();
2087 for (sal_Int32 i = 0; i < n; ++i, ++p)
2089 if (*p == '\\')
2091 // Skip '\'.
2092 ++i;
2093 ++p;
2095 // Parse all subsequent digits until first non-digit is found.
2096 sal_Int32 nDigitCount = 0;
2097 const sal_Unicode* p1 = p;
2098 for (; i < n; ++i, ++p, ++nDigitCount)
2100 if (*p < '0' || '9' < *p)
2102 --i;
2103 --p;
2104 break;
2108 if (nDigitCount)
2110 // Hex-encoded character found. Decode it back into its
2111 // original character. An example of number format with
2112 // hex-encoded chars: "\0022$\0022\#\,\#\#0\.00"
2113 sal_uInt32 nVal = OUString(p1, nDigitCount).toUInt32(16);
2114 aBuf.append(static_cast<sal_Unicode>(nVal));
2117 else
2118 aBuf.append(*p);
2120 return aBuf.makeStringAndClear();
2125 void ScHTMLTable::DataOn( const ImportInfo& rInfo )
2127 PushEntry( rInfo, true );
2128 if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
2130 // read needed options from the <td> tag
2131 ScHTMLSize aSpanSize( 1, 1 );
2132 std::unique_ptr<OUString> pValStr, pNumStr;
2133 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2134 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
2135 sal_uInt32 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
2136 for (; itr != itrEnd; ++itr)
2138 switch (itr->GetToken())
2140 case HTML_O_COLSPAN:
2141 aSpanSize.mnCols = static_cast<SCCOL>( getLimitedValue<sal_Int32>( itr->GetString().toInt32(), 1, 256 ) );
2142 break;
2143 case HTML_O_ROWSPAN:
2144 aSpanSize.mnRows = static_cast<SCROW>( getLimitedValue<sal_Int32>( itr->GetString().toInt32(), 1, 256 ) );
2145 break;
2146 case HTML_O_SDVAL:
2147 pValStr.reset(new OUString(itr->GetString()));
2148 break;
2149 case HTML_O_SDNUM:
2150 pNumStr.reset(new OUString(itr->GetString()));
2151 break;
2152 case HTML_O_CLASS:
2154 // Pick up the number format associated with this class (if
2155 // any).
2156 OUString aElem("td");
2157 OUString aClass = itr->GetString();
2158 OUString aProp("mso-number-format");
2159 const ScHTMLStyles& rStyles = mpParser->GetStyles();
2160 const OUString& rVal = rStyles.getPropertyValue(aElem, aClass, aProp);
2161 if (!rVal.isEmpty())
2163 OUString aNumFmt = decodeNumberFormat(rVal);
2165 nNumberFormat = GetFormatTable()->GetEntryKey(aNumFmt);
2166 if (nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
2168 sal_Int32 nErrPos = 0;
2169 short nDummy;
2170 bool bValidFmt = GetFormatTable()->PutEntry(aNumFmt, nErrPos, nDummy, nNumberFormat);
2171 if (!bValidFmt)
2172 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
2176 break;
2180 ImplDataOn( aSpanSize );
2182 if (nNumberFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
2183 mxDataItemSet->Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat) );
2185 ProcessFormatOptions( *mxDataItemSet, rInfo );
2186 CreateNewEntry( rInfo );
2187 mxCurrEntry->pValStr = pValStr.release();
2188 mxCurrEntry->pNumStr = pNumStr.release();
2190 else
2191 CreateNewEntry( rInfo );
2194 void ScHTMLTable::DataOff( const ImportInfo& rInfo )
2196 PushEntry( rInfo, true );
2197 if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
2198 ImplDataOff();
2199 CreateNewEntry( rInfo );
2202 void ScHTMLTable::BodyOn( const ImportInfo& rInfo )
2204 bool bPushed = PushEntry( rInfo );
2205 if( !mpParentTable )
2207 // do not start new row, if nothing (no title) precedes the body.
2208 if( bPushed || !mbRowOn )
2209 ImplRowOn();
2210 if( bPushed || !mbDataOn )
2211 ImplDataOn( ScHTMLSize( 1, 1 ) );
2212 ProcessFormatOptions( *mxDataItemSet, rInfo );
2214 CreateNewEntry( rInfo );
2217 void ScHTMLTable::BodyOff( const ImportInfo& rInfo )
2219 PushEntry( rInfo );
2220 if( !mpParentTable )
2222 ImplDataOff();
2223 ImplRowOff();
2225 CreateNewEntry( rInfo );
2228 ScHTMLTable* ScHTMLTable::CloseTable( const ImportInfo& rInfo )
2230 if( mpParentTable ) // not allowed to close global table
2232 PushEntry( rInfo, mbDataOn );
2233 ImplDataOff();
2234 ImplRowOff();
2235 mpParentTable->PushTableEntry( GetTableId() );
2236 mpParentTable->CreateNewEntry( rInfo );
2237 if( mbPreFormText ) // enclose preformatted table with empty lines in parent table
2238 mpParentTable->InsertLeadingEmptyLine();
2239 return mpParentTable;
2241 return this;
2244 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
2246 const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2247 size_t nIndex = static_cast< size_t >( nCellPos );
2248 if( nIndex >= rSizes.size() ) return 0;
2249 return (nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]);
2252 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const
2254 const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2255 size_t nBeginIdx = static_cast< size_t >( std::max< SCCOLROW >( nCellBegin, 0 ) );
2256 size_t nEndIdx = static_cast< size_t >( std::min< SCCOLROW >( nCellEnd, static_cast< SCCOLROW >( rSizes.size() ) ) );
2257 if (nBeginIdx >= nEndIdx ) return 0;
2258 return rSizes[ nEndIdx - 1 ] - ((nBeginIdx == 0) ? 0 : rSizes[ nBeginIdx - 1 ]);
2261 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient ) const
2263 const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2264 return rSizes.empty() ? 0 : rSizes.back();
2267 ScHTMLSize ScHTMLTable::GetDocSize( const ScHTMLPos& rCellPos ) const
2269 ScHTMLSize aCellSpan = GetSpan( rCellPos );
2270 return ScHTMLSize(
2271 static_cast< SCCOL >( GetDocSize( tdCol, rCellPos.mnCol, rCellPos.mnCol + aCellSpan.mnCols ) ),
2272 static_cast< SCROW >( GetDocSize( tdRow, rCellPos.mnRow, rCellPos.mnRow + aCellSpan.mnRows ) ) );
2275 SCCOLROW ScHTMLTable::GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
2277 return maDocBasePos.Get( eOrient ) + GetDocSize( eOrient, 0, nCellPos );
2280 ScHTMLPos ScHTMLTable::GetDocPos( const ScHTMLPos& rCellPos ) const
2282 return ScHTMLPos(
2283 static_cast< SCCOL >( GetDocPos( tdCol, rCellPos.mnCol ) ),
2284 static_cast< SCROW >( GetDocPos( tdRow, rCellPos.mnRow ) ) );
2287 void ScHTMLTable::GetDocRange( ScRange& rRange ) const
2289 rRange.aStart = rRange.aEnd = maDocBasePos.MakeAddr();
2290 ScAddress aErrorPos( ScAddress::UNINITIALIZED );
2291 if (!rRange.aEnd.Move( static_cast< SCsCOL >( GetDocSize( tdCol ) ) - 1,
2292 static_cast< SCsROW >( GetDocSize( tdRow ) ) - 1, 0, aErrorPos))
2294 assert(!"can't move");
2298 void ScHTMLTable::ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const
2300 OSL_ENSURE( pDoc, "ScHTMLTable::ApplyCellBorders - no document" );
2301 if( pDoc && mbBorderOn )
2303 const SCCOL nLastCol = maSize.mnCols - 1;
2304 const SCROW nLastRow = maSize.mnRows - 1;
2305 const long nOuterLine = DEF_LINE_WIDTH_2;
2306 const long nInnerLine = DEF_LINE_WIDTH_0;
2307 SvxBorderLine aOuterLine(nullptr, nOuterLine, table::BorderLineStyle::SOLID);
2308 SvxBorderLine aInnerLine(nullptr, nInnerLine, table::BorderLineStyle::SOLID);
2309 SvxBoxItem aBorderItem( ATTR_BORDER );
2311 for( SCCOL nCol = 0; nCol <= nLastCol; ++nCol )
2313 SvxBorderLine* pLeftLine = (nCol == 0) ? &aOuterLine : &aInnerLine;
2314 SvxBorderLine* pRightLine = (nCol == nLastCol) ? &aOuterLine : &aInnerLine;
2315 SCCOL nCellCol1 = static_cast< SCCOL >( GetDocPos( tdCol, nCol ) ) + rFirstPos.Col();
2316 SCCOL nCellCol2 = nCellCol1 + static_cast< SCCOL >( GetDocSize( tdCol, nCol ) ) - 1;
2317 for( SCROW nRow = 0; nRow <= nLastRow; ++nRow )
2319 SvxBorderLine* pTopLine = (nRow == 0) ? &aOuterLine : &aInnerLine;
2320 SvxBorderLine* pBottomLine = (nRow == nLastRow) ? &aOuterLine : &aInnerLine;
2321 SCROW nCellRow1 = GetDocPos( tdRow, nRow ) + rFirstPos.Row();
2322 SCROW nCellRow2 = nCellRow1 + GetDocSize( tdRow, nRow ) - 1;
2323 for( SCCOL nCellCol = nCellCol1; nCellCol <= nCellCol2; ++nCellCol )
2325 aBorderItem.SetLine( (nCellCol == nCellCol1) ? pLeftLine : nullptr, SvxBoxItemLine::LEFT );
2326 aBorderItem.SetLine( (nCellCol == nCellCol2) ? pRightLine : nullptr, SvxBoxItemLine::RIGHT );
2327 for( SCROW nCellRow = nCellRow1; nCellRow <= nCellRow2; ++nCellRow )
2329 aBorderItem.SetLine( (nCellRow == nCellRow1) ? pTopLine : nullptr, SvxBoxItemLine::TOP );
2330 aBorderItem.SetLine( (nCellRow == nCellRow2) ? pBottomLine : nullptr, SvxBoxItemLine::BOTTOM );
2331 pDoc->ApplyAttr( nCellCol, nCellRow, rFirstPos.Tab(), aBorderItem );
2338 for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2339 aIter->ApplyCellBorders( pDoc, rFirstPos );
2342 SvNumberFormatter* ScHTMLTable::GetFormatTable()
2344 return mpParser->GetDoc().GetFormatTable();
2347 bool ScHTMLTable::IsEmptyCell() const
2349 return mpCurrEntryList && mpCurrEntryList->empty();
2352 bool ScHTMLTable::IsSpaceCharInfo( const ImportInfo& rInfo )
2354 return (rInfo.nToken == HTML_TEXTTOKEN) && (rInfo.aText.getLength() == 1) && (rInfo.aText[ 0 ] == ' ');
2357 ScHTMLTable::ScHTMLEntryPtr ScHTMLTable::CreateEntry() const
2359 return o3tl::make_unique<ScHTMLEntry>( GetCurrItemSet() );
2362 void ScHTMLTable::CreateNewEntry( const ImportInfo& rInfo )
2364 OSL_ENSURE( !mxCurrEntry.get(), "ScHTMLTable::CreateNewEntry - old entry still present" );
2365 mxCurrEntry = CreateEntry();
2366 mxCurrEntry->aSel = rInfo.aSelection;
2369 void ScHTMLTable::ImplPushEntryToList( ScHTMLEntryList& rEntryList, ScHTMLEntryPtr& rxEntry )
2371 // HTML entry list does not own the entries
2372 rEntryList.push_back( rxEntry.get() );
2373 // mrEEParseList (reference to member of ScEEParser) owns the entries
2374 mrEEParseList.push_back( rxEntry.release() );
2377 bool ScHTMLTable::PushEntry( ScHTMLEntryPtr& rxEntry )
2379 bool bPushed = false;
2380 if( rxEntry.get() && rxEntry->HasContents() )
2382 if( mpCurrEntryList )
2384 if( mbPushEmptyLine )
2386 ScHTMLEntryPtr xEmptyEntry = CreateEntry();
2387 ImplPushEntryToList( *mpCurrEntryList, xEmptyEntry );
2388 mbPushEmptyLine = false;
2390 ImplPushEntryToList( *mpCurrEntryList, rxEntry );
2391 bPushed = true;
2393 else if( mpParentTable )
2395 bPushed = mpParentTable->PushEntry( rxEntry );
2397 else
2399 OSL_FAIL( "ScHTMLTable::PushEntry - cannot push entry, no parent found" );
2402 return bPushed;
2405 bool ScHTMLTable::PushEntry( const ImportInfo& rInfo, bool bLastInCell )
2407 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::PushEntry - no current entry" );
2408 bool bPushed = false;
2409 if( mxCurrEntry.get() )
2411 mxCurrEntry->AdjustEnd( rInfo );
2412 mxCurrEntry->Strip( mrEditEngine );
2414 // import entry always, if it is the last in cell, and cell is still empty
2415 if( bLastInCell && IsEmptyCell() )
2417 mxCurrEntry->SetImportAlways();
2418 // don't insert empty lines before single empty entries
2419 if( mxCurrEntry->IsEmpty() )
2420 mbPushEmptyLine = false;
2423 bPushed = PushEntry( mxCurrEntry );
2424 mxCurrEntry.reset();
2426 return bPushed;
2429 void ScHTMLTable::PushTableEntry( ScHTMLTableId nTableId )
2431 OSL_ENSURE( nTableId != SC_HTML_GLOBAL_TABLE, "ScHTMLTable::PushTableEntry - cannot push global table" );
2432 if( nTableId != SC_HTML_GLOBAL_TABLE )
2434 ScHTMLEntryPtr xEntry( new ScHTMLEntry( maTableItemSet, nTableId ) );
2435 PushEntry( xEntry );
2439 ScHTMLTable* ScHTMLTable::GetExistingTable( ScHTMLTableId nTableId ) const
2441 ScHTMLTable* pTable = ((nTableId != SC_HTML_GLOBAL_TABLE) && mxNestedTables.get()) ?
2442 mxNestedTables->FindTable( nTableId, false ) : nullptr;
2443 OSL_ENSURE( pTable || (nTableId == SC_HTML_GLOBAL_TABLE), "ScHTMLTable::GetExistingTable - table not found" );
2444 return pTable;
2447 ScHTMLTable* ScHTMLTable::InsertNestedTable( const ImportInfo& rInfo, bool bPreFormText )
2449 if( !mxNestedTables.get() )
2450 mxNestedTables.reset( new ScHTMLTableMap( *this ) );
2451 if( bPreFormText ) // enclose new preformatted table with empty lines
2452 InsertLeadingEmptyLine();
2453 return mxNestedTables->CreateTable( rInfo, bPreFormText );
2456 void ScHTMLTable::InsertNewCell( const ScHTMLSize& rSpanSize )
2458 ScRange* pRange;
2460 /* Find an unused cell by skipping all merged ranges that cover the
2461 current cell position stored in maCurrCell. */
2462 while( ((pRange = maVMergedCells.Find( maCurrCell.MakeAddr() )) != nullptr) || ((pRange = maHMergedCells.Find( maCurrCell.MakeAddr() )) != nullptr) )
2463 maCurrCell.mnCol = pRange->aEnd.Col() + 1;
2464 mpCurrEntryList = &maEntryMap[ maCurrCell ];
2466 /* If the new cell is merged horizontally, try to find collisions with
2467 other vertically merged ranges. In this case, shrink existing
2468 vertically merged ranges (do not shrink the new cell). */
2469 SCCOL nColEnd = maCurrCell.mnCol + rSpanSize.mnCols;
2470 for( ScAddress aAddr( maCurrCell.MakeAddr() ); aAddr.Col() < nColEnd; aAddr.IncCol() )
2471 if( (pRange = maVMergedCells.Find( aAddr )) != nullptr )
2472 pRange->aEnd.SetRow( maCurrCell.mnRow - 1 );
2474 // insert the new range into the cell lists
2475 ScRange aNewRange( maCurrCell.MakeAddr() );
2476 ScAddress aErrorPos( ScAddress::UNINITIALIZED );
2477 if (!aNewRange.aEnd.Move( rSpanSize.mnCols - 1, rSpanSize.mnRows - 1, 0, aErrorPos))
2479 assert(!"can't move");
2481 if( rSpanSize.mnRows > 1 )
2483 maVMergedCells.Append( aNewRange );
2484 /* Do not insert vertically merged ranges into maUsedCells yet,
2485 because they may be shrunken (see above). The final vertically
2486 merged ranges are inserted in FillEmptyCells(). */
2488 else
2490 if( rSpanSize.mnCols > 1 )
2491 maHMergedCells.Append( aNewRange );
2492 /* Insert horizontally merged ranges and single cells into
2493 maUsedCells, they will not be changed anymore. */
2494 maUsedCells.Join( aNewRange );
2497 // adjust table size
2498 maSize.mnCols = std::max< SCCOL >( maSize.mnCols, aNewRange.aEnd.Col() + 1 );
2499 maSize.mnRows = std::max< SCROW >( maSize.mnRows, aNewRange.aEnd.Row() + 1 );
2502 void ScHTMLTable::ImplRowOn()
2504 if( mbRowOn )
2505 ImplRowOff();
2506 mxRowItemSet.reset( new SfxItemSet( maTableItemSet ) );
2507 maCurrCell.mnCol = 0;
2508 mbRowOn = true;
2509 mbDataOn = false;
2512 void ScHTMLTable::ImplRowOff()
2514 if( mbDataOn )
2515 ImplDataOff();
2516 if( mbRowOn )
2518 mxRowItemSet.reset();
2519 ++maCurrCell.mnRow;
2520 mbRowOn = mbDataOn = false;
2524 void ScHTMLTable::ImplDataOn( const ScHTMLSize& rSpanSize )
2526 if( mbDataOn )
2527 ImplDataOff();
2528 if( !mbRowOn )
2529 ImplRowOn();
2530 mxDataItemSet.reset( new SfxItemSet( *mxRowItemSet ) );
2531 InsertNewCell( rSpanSize );
2532 mbDataOn = true;
2533 mbPushEmptyLine = false;
2536 void ScHTMLTable::ImplDataOff()
2538 if( mbDataOn )
2540 mxDataItemSet.reset();
2541 ++maCurrCell.mnCol;
2542 mpCurrEntryList = nullptr;
2543 mbDataOn = false;
2547 void ScHTMLTable::ProcessFormatOptions( SfxItemSet& rItemSet, const ImportInfo& rInfo )
2549 // special handling for table header cells
2550 if( rInfo.nToken == HTML_TABLEHEADER_ON )
2552 rItemSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2553 rItemSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY ) );
2556 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2557 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
2558 for (; itr != itrEnd; ++itr)
2560 switch( itr->GetToken() )
2562 case HTML_O_ALIGN:
2564 SvxCellHorJustify eVal = SVX_HOR_JUSTIFY_STANDARD;
2565 const OUString& rOptVal = itr->GetString();
2566 if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
2567 eVal = SVX_HOR_JUSTIFY_RIGHT;
2568 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
2569 eVal = SVX_HOR_JUSTIFY_CENTER;
2570 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
2571 eVal = SVX_HOR_JUSTIFY_LEFT;
2572 if( eVal != SVX_HOR_JUSTIFY_STANDARD )
2573 rItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY ) );
2575 break;
2577 case HTML_O_VALIGN:
2579 SvxCellVerJustify eVal = SVX_VER_JUSTIFY_STANDARD;
2580 const OUString& rOptVal = itr->GetString();
2581 if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
2582 eVal = SVX_VER_JUSTIFY_TOP;
2583 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
2584 eVal = SVX_VER_JUSTIFY_CENTER;
2585 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
2586 eVal = SVX_VER_JUSTIFY_BOTTOM;
2587 if( eVal != SVX_VER_JUSTIFY_STANDARD )
2588 rItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY ) );
2590 break;
2592 case HTML_O_BGCOLOR:
2594 Color aColor;
2595 itr->GetColor( aColor );
2596 rItemSet.Put( SvxBrushItem( aColor, ATTR_BACKGROUND ) );
2598 break;
2603 void ScHTMLTable::SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize )
2605 OSL_ENSURE( nCellPos >= 0, "ScHTMLTable::SetDocSize - unexpected negative position" );
2606 ScSizeVec& rSizes = maCumSizes[ eOrient ];
2607 size_t nIndex = static_cast< size_t >( nCellPos );
2608 // expand with height/width == 1
2609 while( nIndex >= rSizes.size() )
2610 rSizes.push_back( rSizes.empty() ? 1 : (rSizes.back() + 1) );
2611 // update size of passed position and all following
2612 // #i109987# only grow, don't shrink - use the largest needed size
2613 SCsCOLROW nDiff = nSize - ((nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]));
2614 if( nDiff > 0 )
2615 for( ScSizeVec::iterator aIt = rSizes.begin() + nIndex, aEnd = rSizes.end(); aIt != aEnd; ++aIt )
2616 *aIt += nDiff;
2619 void ScHTMLTable::CalcNeededDocSize(
2620 ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nCellSpan, SCCOLROW nRealDocSize )
2622 SCCOLROW nDiffSize = 0;
2623 // in merged columns/rows: reduce needed size by size of leading columns
2624 while( nCellSpan > 1 )
2626 nDiffSize += GetDocSize( eOrient, nCellPos );
2627 --nCellSpan;
2628 ++nCellPos;
2630 // set remaining needed size to last column/row
2631 nRealDocSize -= std::min< SCCOLROW >( nRealDocSize - 1, nDiffSize );
2632 SetDocSize( eOrient, nCellPos, nRealDocSize );
2635 void ScHTMLTable::FillEmptyCells()
2637 for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2638 aIter->FillEmptyCells();
2640 // insert the final vertically merged ranges into maUsedCells
2641 for ( size_t i = 0, nRanges = maVMergedCells.size(); i < nRanges; ++i )
2643 ScRange* pRange = maVMergedCells[ i ];
2644 maUsedCells.Join( *pRange );
2647 for( ScAddress aAddr; aAddr.Row() < maSize.mnRows; aAddr.IncRow() )
2649 for( aAddr.SetCol( 0 ); aAddr.Col() < maSize.mnCols; aAddr.IncCol() )
2651 if( !maUsedCells.Find( aAddr ) )
2653 // create a range for the lock list (used to calc. cell span)
2654 ScRange aRange( aAddr );
2657 aRange.aEnd.IncCol();
2659 while( (aRange.aEnd.Col() < maSize.mnCols) && !maUsedCells.Find( aRange.aEnd ) );
2660 aRange.aEnd.IncCol( -1 );
2661 maUsedCells.Join( aRange );
2663 // insert a dummy entry
2664 ScHTMLEntryPtr xEntry = CreateEntry();
2665 ImplPushEntryToList( maEntryMap[ ScHTMLPos( aAddr ) ], xEntry );
2671 void ScHTMLTable::RecalcDocSize()
2673 // recalc table sizes recursively from inner to outer
2674 for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2675 aIter->RecalcDocSize();
2677 /* Two passes: first calculates the sizes of single columns/rows, then
2678 the sizes of spanned columns/rows. This allows to fill nested tables
2679 into merged cells optimally. */
2680 static const sal_uInt16 PASS_SINGLE = 0;
2681 static const sal_uInt16 PASS_SPANNED = 1;
2682 for( sal_uInt16 nPass = PASS_SINGLE; nPass <= PASS_SPANNED; ++nPass )
2684 // iterate through every table cell
2685 ScHTMLEntryMap::const_iterator aMapIterEnd = maEntryMap.end();
2686 for( ScHTMLEntryMap::const_iterator aMapIter = maEntryMap.begin(); aMapIter != aMapIterEnd; ++aMapIter )
2688 const ScHTMLPos& rCellPos = aMapIter->first;
2689 ScHTMLSize aCellSpan = GetSpan( rCellPos );
2691 const ScHTMLEntryList& rEntryList = aMapIter->second;
2692 ScHTMLEntryList::const_iterator aListIter;
2693 ScHTMLEntryList::const_iterator aListIterEnd = rEntryList.end();
2695 // process the dimension of the current cell in this pass?
2696 // (pass is single and span is 1) or (pass is not single and span is not 1)
2697 bool bProcessColWidth = ((nPass == PASS_SINGLE) == (aCellSpan.mnCols == 1));
2698 bool bProcessRowHeight = ((nPass == PASS_SINGLE) == (aCellSpan.mnRows == 1));
2699 if( bProcessColWidth || bProcessRowHeight )
2701 ScHTMLSize aDocSize( 1, 0 ); // resulting size of the cell in document
2703 // expand the cell size for each cell parse entry
2704 for( aListIter = rEntryList.begin(); aListIter != aListIterEnd; ++aListIter )
2706 ScHTMLTable* pTable = GetExistingTable( (*aListIter)->GetTableId() );
2707 // find entry with maximum width
2708 if( bProcessColWidth && pTable )
2709 aDocSize.mnCols = std::max( aDocSize.mnCols, static_cast< SCCOL >( pTable->GetDocSize( tdCol ) ) );
2710 // add up height of each entry
2711 if( bProcessRowHeight )
2712 aDocSize.mnRows += pTable ? pTable->GetDocSize( tdRow ) : 1;
2714 if( !aDocSize.mnRows )
2715 aDocSize.mnRows = 1;
2717 if( bProcessColWidth )
2718 CalcNeededDocSize( tdCol, rCellPos.mnCol, aCellSpan.mnCols, aDocSize.mnCols );
2719 if( bProcessRowHeight )
2720 CalcNeededDocSize( tdRow, rCellPos.mnRow, aCellSpan.mnRows, aDocSize.mnRows );
2726 void ScHTMLTable::RecalcDocPos( const ScHTMLPos& rBasePos )
2728 maDocBasePos = rBasePos;
2729 // after the previous assignment it is allowed to call GetDocPos() methods
2731 // iterate through every table cell
2732 ScHTMLEntryMap::iterator aMapIterEnd = maEntryMap.end();
2733 for( ScHTMLEntryMap::iterator aMapIter = maEntryMap.begin(); aMapIter != aMapIterEnd; ++aMapIter )
2735 // fixed doc position of the entire cell (first entry)
2736 const ScHTMLPos aCellDocPos( GetDocPos( aMapIter->first ) );
2737 // fixed doc size of the entire cell
2738 const ScHTMLSize aCellDocSize( GetDocSize( aMapIter->first ) );
2740 // running doc position for single entries
2741 ScHTMLPos aEntryDocPos( aCellDocPos );
2743 ScHTMLEntryList& rEntryList = aMapIter->second;
2744 ScHTMLEntry* pEntry = nullptr;
2745 ScHTMLEntryList::iterator aListIterEnd = rEntryList.end();
2746 for( ScHTMLEntryList::iterator aListIter = rEntryList.begin(); aListIter != aListIterEnd; ++aListIter )
2748 pEntry = *aListIter;
2749 if( ScHTMLTable* pTable = GetExistingTable( pEntry->GetTableId() ) )
2751 pTable->RecalcDocPos( aEntryDocPos ); // recalc nested table
2752 pEntry->nCol = SCCOL_MAX;
2753 pEntry->nRow = SCROW_MAX;
2754 SCROW nTableRows = static_cast< SCROW >( pTable->GetDocSize( tdRow ) );
2756 // use this entry to pad empty space right of table
2757 if( mpParentTable ) // ... but not in global table
2759 SCCOL nStartCol = aEntryDocPos.mnCol + static_cast< SCCOL >( pTable->GetDocSize( tdCol ) );
2760 SCCOL nNextCol = aEntryDocPos.mnCol + aCellDocSize.mnCols;
2761 if( nStartCol < nNextCol )
2763 pEntry->nCol = nStartCol;
2764 pEntry->nRow = aEntryDocPos.mnRow;
2765 pEntry->nColOverlap = nNextCol - nStartCol;
2766 pEntry->nRowOverlap = nTableRows;
2769 aEntryDocPos.mnRow += nTableRows;
2771 else
2773 pEntry->nCol = aEntryDocPos.mnCol;
2774 pEntry->nRow = aEntryDocPos.mnRow;
2775 if( mpParentTable ) // do not merge in global table
2776 pEntry->nColOverlap = aCellDocSize.mnCols;
2777 ++aEntryDocPos.mnRow;
2781 // pEntry points now to last entry.
2782 if( pEntry )
2784 if( (pEntry == rEntryList.front()) && (pEntry->GetTableId() == SC_HTML_NO_TABLE) )
2786 // pEntry is the only entry in this cell - merge rows of cell with single non-table entry.
2787 pEntry->nRowOverlap = aCellDocSize.mnRows;
2789 else
2791 // fill up incomplete entry lists
2792 SCROW nFirstUnusedRow = aCellDocPos.mnRow + aCellDocSize.mnRows;
2793 while( aEntryDocPos.mnRow < nFirstUnusedRow )
2795 ScHTMLEntryPtr xDummyEntry( new ScHTMLEntry( pEntry->GetItemSet() ) );
2796 xDummyEntry->nCol = aEntryDocPos.mnCol;
2797 xDummyEntry->nRow = aEntryDocPos.mnRow;
2798 xDummyEntry->nColOverlap = aCellDocSize.mnCols;
2799 ImplPushEntryToList( rEntryList, xDummyEntry );
2800 ++aEntryDocPos.mnRow;
2807 ScHTMLGlobalTable::ScHTMLGlobalTable(
2808 SfxItemPool& rPool,
2809 EditEngine& rEditEngine,
2810 std::vector< ScEEParseEntry* >& rEEParseList,
2811 ScHTMLTableId& rnUnusedId,
2812 ScHTMLParser* pParser
2814 ScHTMLTable( rPool, rEditEngine, rEEParseList, rnUnusedId, pParser )
2818 ScHTMLGlobalTable::~ScHTMLGlobalTable()
2822 void ScHTMLGlobalTable::Recalc()
2824 // Fills up empty cells with a dummy entry. */
2825 FillEmptyCells();
2826 // recalc table sizes of all nested tables and this table
2827 RecalcDocSize();
2828 // recalc document positions of all entries in this table and in nested tables
2829 RecalcDocPos( GetDocPos() );
2832 ScHTMLQueryParser::ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
2833 ScHTMLParser( pEditEngine, pDoc ),
2834 mnUnusedId( SC_HTML_GLOBAL_TABLE ),
2835 mbTitleOn( false )
2837 mxGlobTable.reset(
2838 new ScHTMLGlobalTable(*pPool, *pEdit, maList, mnUnusedId, this));
2839 mpCurrTable = mxGlobTable.get();
2842 ScHTMLQueryParser::~ScHTMLQueryParser()
2846 sal_uLong ScHTMLQueryParser::Read( SvStream& rStrm, const OUString& rBaseURL )
2848 SvKeyValueIteratorRef xValues;
2849 SvKeyValueIterator* pAttributes = nullptr;
2851 SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
2852 if( pObjSh && pObjSh->IsLoading() )
2854 pAttributes = pObjSh->GetHeaderAttributes();
2856 else
2858 /* When not loading, set up fake HTTP headers to force the SfxHTMLParser
2859 to use UTF8 (used when pasting from clipboard) */
2860 const sal_Char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
2861 if( pCharSet )
2863 OUString aContentType = "text/html; charset=";
2864 aContentType += OUString::createFromAscii( pCharSet );
2866 xValues = new SvKeyValueIterator;
2867 xValues->Append( SvKeyValue( OUString( OOO_STRING_SVTOOLS_HTML_META_content_type ), aContentType ) );
2868 pAttributes = xValues;
2872 Link<ImportInfo&,void> aOldLink = pEdit->GetImportHdl();
2873 pEdit->SetImportHdl( LINK( this, ScHTMLQueryParser, HTMLImportHdl ) );
2874 sal_uLong nErr = pEdit->Read( rStrm, rBaseURL, EE_FORMAT_HTML, pAttributes );
2875 pEdit->SetImportHdl( aOldLink );
2877 mxGlobTable->Recalc();
2878 nColMax = static_cast< SCCOL >( mxGlobTable->GetDocSize( tdCol ) - 1 );
2879 nRowMax = static_cast< SCROW >( mxGlobTable->GetDocSize( tdRow ) - 1 );
2881 return nErr;
2884 const ScHTMLTable* ScHTMLQueryParser::GetGlobalTable() const
2886 return mxGlobTable.get();
2889 void ScHTMLQueryParser::ProcessToken( const ImportInfo& rInfo )
2891 switch( rInfo.nToken )
2893 // --- meta data ---
2894 case HTML_META: MetaOn( rInfo ); break; // <meta>
2896 // --- title handling ---
2897 case HTML_TITLE_ON: TitleOn( rInfo ); break; // <title>
2898 case HTML_TITLE_OFF: TitleOff( rInfo ); break; // </title>
2900 case HTML_STYLE_ON: break;
2901 case HTML_STYLE_OFF: ParseStyle(rInfo.aText); break;
2903 // --- body handling ---
2904 case HTML_BODY_ON: mpCurrTable->BodyOn( rInfo ); break; // <body>
2905 case HTML_BODY_OFF: mpCurrTable->BodyOff( rInfo ); break; // </body>
2907 // --- insert text ---
2908 case HTML_TEXTTOKEN: InsertText( rInfo ); break; // any text
2909 case HTML_LINEBREAK: mpCurrTable->BreakOn(); break; // <br>
2910 case HTML_HEAD1_ON: // <h1>
2911 case HTML_HEAD2_ON: // <h2>
2912 case HTML_HEAD3_ON: // <h3>
2913 case HTML_HEAD4_ON: // <h4>
2914 case HTML_HEAD5_ON: // <h5>
2915 case HTML_HEAD6_ON: // <h6>
2916 case HTML_PARABREAK_ON: mpCurrTable->HeadingOn(); break; // <p>
2918 // --- misc. contents ---
2919 case HTML_ANCHOR_ON: mpCurrTable->AnchorOn(); break; // <a>
2921 // --- table handling ---
2922 case HTML_TABLE_ON: TableOn( rInfo ); break; // <table>
2923 case HTML_TABLE_OFF: TableOff( rInfo ); break; // </table>
2924 case HTML_TABLEROW_ON: mpCurrTable->RowOn( rInfo ); break; // <tr>
2925 case HTML_TABLEROW_OFF: mpCurrTable->RowOff( rInfo ); break; // </tr>
2926 case HTML_TABLEHEADER_ON: // <th>
2927 case HTML_TABLEDATA_ON: mpCurrTable->DataOn( rInfo ); break; // <td>
2928 case HTML_TABLEHEADER_OFF: // </th>
2929 case HTML_TABLEDATA_OFF: mpCurrTable->DataOff( rInfo ); break; // </td>
2930 case HTML_PREFORMTXT_ON: PreOn( rInfo ); break; // <pre>
2931 case HTML_PREFORMTXT_OFF: PreOff( rInfo ); break; // </pre>
2933 // --- formatting ---
2934 case HTML_FONT_ON: FontOn( rInfo ); break; // <font>
2936 case HTML_BIGPRINT_ON: // <big>
2937 //! TODO: store current font size, use following size
2938 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 3 ], 100, ATTR_FONT_HEIGHT ) );
2939 break;
2940 case HTML_SMALLPRINT_ON: // <small>
2941 //! TODO: store current font size, use preceding size
2942 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 0 ], 100, ATTR_FONT_HEIGHT ) );
2943 break;
2945 case HTML_BOLD_ON: // <b>
2946 case HTML_STRONG_ON: // <strong>
2947 mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2948 break;
2950 case HTML_ITALIC_ON: // <i>
2951 case HTML_EMPHASIS_ON: // <em>
2952 case HTML_ADDRESS_ON: // <address>
2953 case HTML_BLOCKQUOTE_ON: // <blockquote>
2954 case HTML_BLOCKQUOTE30_ON: // <bq>
2955 case HTML_CITIATION_ON: // <cite>
2956 case HTML_VARIABLE_ON: // <var>
2957 mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
2958 break;
2960 case HTML_DEFINSTANCE_ON: // <dfn>
2961 mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2962 mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
2963 break;
2965 case HTML_UNDERLINE_ON: // <u>
2966 mpCurrTable->PutItem( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
2967 break;
2971 void ScHTMLQueryParser::InsertText( const ImportInfo& rInfo )
2973 mpCurrTable->PutText( rInfo );
2974 if( mbTitleOn )
2975 maTitle.append(rInfo.aText);
2978 void ScHTMLQueryParser::FontOn( const ImportInfo& rInfo )
2980 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2981 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
2982 for (; itr != itrEnd; ++itr)
2984 switch( itr->GetToken() )
2986 case HTML_O_FACE :
2988 const OUString& rFace = itr->GetString();
2989 OUString aFontName;
2990 sal_Int32 nPos = 0;
2991 while( nPos != -1 )
2993 // font list separator: VCL = ';' HTML = ','
2994 OUString aFName = comphelper::string::strip(rFace.getToken(0, ',', nPos), ' ');
2995 aFontName = ScGlobal::addToken(aFontName, aFName, ';');
2997 if ( !aFontName.isEmpty() )
2998 mpCurrTable->PutItem( SvxFontItem( FAMILY_DONTKNOW,
2999 aFontName, EMPTY_OUSTRING, PITCH_DONTKNOW,
3000 RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
3002 break;
3003 case HTML_O_SIZE :
3005 sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( itr->GetNumber(), 1, SC_HTML_FONTSIZES );
3006 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ nSize - 1 ], 100, ATTR_FONT_HEIGHT ) );
3008 break;
3009 case HTML_O_COLOR :
3011 Color aColor;
3012 itr->GetColor( aColor );
3013 mpCurrTable->PutItem( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
3015 break;
3020 void ScHTMLQueryParser::MetaOn( const ImportInfo& rInfo )
3022 if( mpDoc->GetDocumentShell() )
3024 HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
3026 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3027 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
3028 pParser->ParseMetaOptions(
3029 xDPS->getDocumentProperties(),
3030 mpDoc->GetDocumentShell()->GetHeaderAttributes() );
3034 void ScHTMLQueryParser::TitleOn( const ImportInfo& /*rInfo*/ )
3036 mbTitleOn = true;
3037 maTitle.makeStringAndClear();
3040 void ScHTMLQueryParser::TitleOff( const ImportInfo& rInfo )
3042 if( mbTitleOn )
3044 OUString aTitle = maTitle.makeStringAndClear().trim();
3045 if (!aTitle.isEmpty() && mpDoc->GetDocumentShell())
3047 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3048 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
3050 xDPS->getDocumentProperties()->setTitle(aTitle);
3052 InsertText( rInfo );
3053 mbTitleOn = false;
3057 void ScHTMLQueryParser::TableOn( const ImportInfo& rInfo )
3059 mpCurrTable = mpCurrTable->TableOn( rInfo );
3062 void ScHTMLQueryParser::TableOff( const ImportInfo& rInfo )
3064 mpCurrTable = mpCurrTable->TableOff( rInfo );
3067 void ScHTMLQueryParser::PreOn( const ImportInfo& rInfo )
3069 mpCurrTable = mpCurrTable->PreOn( rInfo );
3072 void ScHTMLQueryParser::PreOff( const ImportInfo& rInfo )
3074 mpCurrTable = mpCurrTable->PreOff( rInfo );
3077 void ScHTMLQueryParser::CloseTable( const ImportInfo& rInfo )
3079 mpCurrTable = mpCurrTable->CloseTable( rInfo );
3082 #if ENABLE_ORCUS
3084 namespace {
3087 * Handler class for the CSS parser.
3089 class CSSHandler
3091 struct MemStr
3093 const char* mp;
3094 size_t mn;
3096 MemStr() : mp(nullptr), mn(0) {}
3097 MemStr(const char* p, size_t n) : mp(p), mn(n) {}
3098 MemStr& operator=(const MemStr& r)
3100 mp = r.mp;
3101 mn = r.mn;
3102 return *this;
3106 typedef std::pair<MemStr, MemStr> SelectorName; // element : class
3107 typedef std::vector<SelectorName> SelectorNames;
3108 SelectorNames maSelectorNames; /// current selector names.
3109 MemStr maPropName; /// current property name.
3110 MemStr maPropValue; /// current property value.
3112 ScHTMLStyles& mrStyles;
3113 public:
3114 explicit CSSHandler(ScHTMLStyles& rStyles) : mrStyles(rStyles) {}
3116 static void at_rule_name(const char* /*p*/, size_t /*n*/)
3118 // TODO: For now, we ignore at-rule properties.
3121 void property_name(const char* p, size_t n)
3123 maPropName = MemStr(p, n);
3126 void value(const char* p, size_t n)
3128 maPropValue = MemStr(p, n);
3131 static void begin_parse() {}
3133 static void end_parse() {}
3135 static void begin_block() {}
3137 void end_block()
3139 maSelectorNames.clear();
3142 static void begin_property() {}
3144 void end_property()
3146 SelectorNames::const_iterator itr = maSelectorNames.begin(), itrEnd = maSelectorNames.end();
3147 for (; itr != itrEnd; ++itr)
3149 // Add this property to the collection for each selector.
3150 const SelectorName& rSelName = *itr;
3151 const MemStr& rElem = rSelName.first;
3152 const MemStr& rClass = rSelName.second;
3153 OUString aName(maPropName.mp, maPropName.mn, RTL_TEXTENCODING_UTF8);
3154 OUString aValue(maPropValue.mp, maPropValue.mn, RTL_TEXTENCODING_UTF8);
3155 mrStyles.add(rElem.mp, rElem.mn, rClass.mp, rClass.mn, aName, aValue);
3157 maPropName = MemStr();
3158 maPropValue = MemStr();
3161 // new members
3162 static void simple_selector_type(const char* /*p*/, size_t /*n*/) {}
3164 static void simple_selector_class(const char* /*p*/, size_t /*n*/) {}
3166 static void simple_selector_pseudo_element(orcus::css::pseudo_element_t /*pe*/) {}
3168 static void simple_selector_pseudo_class(orcus::css::pseudo_class_t /*pc*/) {}
3170 static void simple_selector_id(const char* /*p*/, size_t /*n*/) {}
3172 static void end_simple_selector() {}
3174 static void end_selector() {}
3176 static void combinator(orcus::css::combinator_t /*combinator*/) {}
3178 static void rgb(uint8_t /*red*/ , uint8_t /*green*/ , uint8_t /*blue*/ ) {}
3180 static void rgba(uint8_t /*red*/ , uint8_t /*green*/ , uint8_t /*blue*/ , double /*alpha*/ ) {}
3182 static void hsl(uint8_t /*hue*/ , uint8_t /*sat*/ , uint8_t /*light*/ ) {}
3184 static void hsla(uint8_t /*hue*/ , uint8_t /*sat*/ , uint8_t /*light*/ , double /*alpha*/ ) {}
3186 static void url(const char* /*p*/, size_t /*n*/) {}
3192 void ScHTMLQueryParser::ParseStyle(const OUString& rStrm)
3194 OString aStr = OUStringToOString(rStrm, RTL_TEXTENCODING_UTF8);
3195 CSSHandler aHdl(GetStyles());
3196 orcus::css_parser<CSSHandler> aParser(aStr.getStr(), aStr.getLength(), aHdl);
3199 aParser.parse();
3201 catch (const orcus::css::parse_error&)
3203 // TODO: Parsing of CSS failed. Do nothing for now.
3207 #else
3209 void ScHTMLQueryParser::ParseStyle(const OUString&) {}
3211 #endif
3213 IMPL_LINK_TYPED( ScHTMLQueryParser, HTMLImportHdl, ImportInfo&, rInfo, void )
3215 switch( rInfo.eState )
3217 case HTMLIMP_START:
3218 break;
3220 case HTMLIMP_NEXTTOKEN:
3221 case HTMLIMP_UNKNOWNATTR:
3222 ProcessToken( rInfo );
3223 break;
3225 case HTMLIMP_INSERTPARA:
3226 mpCurrTable->InsertPara( rInfo );
3227 break;
3229 case HTMLIMP_SETATTR:
3230 case HTMLIMP_INSERTTEXT:
3231 case HTMLIMP_INSERTFIELD:
3232 break;
3234 case HTMLIMP_END:
3235 while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
3236 CloseTable( rInfo );
3237 break;
3239 default:
3240 OSL_FAIL( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
3244 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */