fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / filter / html / htmlpars.cxx
blob57d0743f16427cfef6247d8fbb02483dbe2b8116
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 <utility>
24 #include <boost/shared_ptr.hpp>
25 #include <comphelper/string.hxx>
26 #include <o3tl/ptr_container.hxx>
28 #include "scitems.hxx"
29 #include <editeng/eeitem.hxx>
31 #include <svtools/htmlcfg.hxx>
32 #include <svx/algitem.hxx>
33 #include <editeng/colritem.hxx>
34 #include <editeng/brushitem.hxx>
35 #include <editeng/editeng.hxx>
36 #include <editeng/fhgtitem.hxx>
37 #include <editeng/fontitem.hxx>
38 #include <editeng/postitem.hxx>
39 #include <editeng/udlnitem.hxx>
40 #include <editeng/wghtitem.hxx>
41 #include <editeng/boxitem.hxx>
42 #include <editeng/justifyitem.hxx>
43 #include <sfx2/objsh.hxx>
44 #include <svl/eitem.hxx>
45 #include <svl/intitem.hxx>
46 #include <vcl/graphicfilter.hxx>
47 #include <svtools/parhtml.hxx>
48 #include <svtools/htmlkywd.hxx>
49 #include <svtools/htmltokn.h>
50 #include <sfx2/docfile.hxx>
52 #include <vcl/svapp.hxx>
53 #include <tools/urlobj.hxx>
54 #include <tools/tenccvt.hxx>
56 #include <rtl/tencinfo.h>
58 #include "htmlpars.hxx"
59 #include "global.hxx"
60 #include "document.hxx"
61 #include "rangelst.hxx"
63 #include <config_orcus.h>
65 #if ENABLE_ORCUS
66 #include <orcus/css_parser.hpp>
67 #endif
69 #include <com/sun/star/document/XDocumentProperties.hpp>
70 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
71 #include <boost/scoped_array.hpp>
73 using ::editeng::SvxBorderLine;
74 using namespace ::com::sun::star;
76 ScHTMLStyles::ScHTMLStyles() : maEmpty() {}
78 void ScHTMLStyles::add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
79 const OUString& aProp, const OUString& aValue)
81 if (pElemName)
83 OUString aElem(pElemName, nElemName, RTL_TEXTENCODING_UTF8);
84 aElem = aElem.toAsciiLowerCase();
85 if (pClassName)
87 // Both element and class names given.
88 ElemsType::iterator itrElem = maElemProps.find(aElem);
89 if (itrElem == maElemProps.end())
91 // new element
92 std::unique_ptr<NamePropsType> p(new NamePropsType);
93 std::pair<ElemsType::iterator, bool> r = o3tl::ptr_container::insert(maElemProps, aElem, std::move(p));
94 if (!r.second)
95 // insertion failed.
96 return;
97 itrElem = r.first;
100 NamePropsType* pClsProps = itrElem->second;
101 OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
102 aClass = aClass.toAsciiLowerCase();
103 insertProp(*pClsProps, aClass, aProp, aValue);
105 else
107 // Element name only. Add it to the element global.
108 insertProp(maElemGlobalProps, aElem, aProp, aValue);
111 else
113 if (pClassName)
115 // Class name only. Add it to the global.
116 OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
117 aClass = aClass.toAsciiLowerCase();
118 insertProp(maGlobalProps, aClass, aProp, aValue);
123 const OUString& ScHTMLStyles::getPropertyValue(
124 const OUString& rElem, const OUString& rClass, const OUString& rPropName) const
126 // First, look into the element-class storage.
128 ElemsType::const_iterator itr = maElemProps.find(rElem);
129 if (itr != maElemProps.end())
131 const NamePropsType* pClasses = itr->second;
132 NamePropsType::const_iterator itr2 = pClasses->find(rClass);
133 if (itr2 != pClasses->end())
135 const PropsType* pProps = itr2->second;
136 PropsType::const_iterator itr3 = pProps->find(rPropName);
137 if (itr3 != pProps->end())
138 return itr3->second;
142 // Next, look into the class global storage.
144 NamePropsType::const_iterator itr = maGlobalProps.find(rClass);
145 if (itr != maGlobalProps.end())
147 const PropsType* pProps = itr->second;
148 PropsType::const_iterator itr2 = pProps->find(rPropName);
149 if (itr2 != pProps->end())
150 return itr2->second;
153 // As the last resort, look into the element global storage.
155 NamePropsType::const_iterator itr = maElemGlobalProps.find(rClass);
156 if (itr != maElemGlobalProps.end())
158 const PropsType* pProps = itr->second;
159 PropsType::const_iterator itr2 = pProps->find(rPropName);
160 if (itr2 != pProps->end())
161 return itr2->second;
165 return maEmpty; // nothing found.
168 void ScHTMLStyles::insertProp(
169 NamePropsType& rStore, const OUString& aName,
170 const OUString& aProp, const OUString& aValue)
172 NamePropsType::iterator itr = rStore.find(aName);
173 if (itr == rStore.end())
175 // new element
176 std::unique_ptr<PropsType> p(new PropsType);
177 std::pair<NamePropsType::iterator, bool> r = o3tl::ptr_container::insert(rStore, aName, std::move(p));
178 if (!r.second)
179 // insertion failed.
180 return;
182 itr = r.first;
185 PropsType* pProps = itr->second;
186 pProps->insert(PropsType::value_type(aProp, aValue));
189 // BASE class for HTML parser classes
191 ScHTMLParser::ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
192 ScEEParser( pEditEngine ),
193 mpDoc( pDoc )
195 SvxHtmlOptions& rHtmlOptions = SvxHtmlOptions::Get();
196 for( sal_uInt16 nIndex = 0; nIndex < SC_HTML_FONTSIZES; ++nIndex )
197 maFontHeights[ nIndex ] = rHtmlOptions.GetFontSize( nIndex ) * 20;
200 ScHTMLParser::~ScHTMLParser()
204 ScHTMLLayoutParser::ScHTMLLayoutParser(
205 EditEngine* pEditP, const OUString& rBaseURL, const Size& aPageSizeP,
206 ScDocument* pDocP ) :
207 ScHTMLParser( pEditP, pDocP ),
208 aPageSize( aPageSizeP ),
209 aBaseURL( rBaseURL ),
210 xLockedList( new ScRangeList ),
211 pTables( NULL ),
212 pColOffset( new ScHTMLColOffset ),
213 pLocalColOffset( new ScHTMLColOffset ),
214 nFirstTableCell(0),
215 nTableLevel(0),
216 nTable(0),
217 nMaxTable(0),
218 nColCntStart(0),
219 nMaxCol(0),
220 nTableWidth(0),
221 nColOffset(0),
222 nColOffsetStart(0),
223 nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
224 bTabInTabCell( false ),
225 bFirstRow( true ),
226 bInCell( false ),
227 bInTitle( false )
229 MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
230 MakeColNoRef( pColOffset, 0, 0, 0, 0 );
233 ScHTMLLayoutParser::~ScHTMLLayoutParser()
235 while ( !aTableStack.empty() )
237 ScHTMLTableStackEntry* pS = aTableStack.top();
238 aTableStack.pop();
240 bool found = false;
241 for ( size_t i = 0, nListSize = maList.size(); i < nListSize; ++i )
243 if ( pS->pCellEntry == maList[ i ] )
245 found = true;
246 break;
249 if ( !found )
250 delete pS->pCellEntry;
251 if ( pS->pLocalColOffset != pLocalColOffset )
252 delete pS->pLocalColOffset;
253 delete pS;
255 delete pLocalColOffset;
256 delete pColOffset;
257 if ( pTables )
259 for( OuterMap::const_iterator it = pTables->begin(); it != pTables->end(); ++it)
260 delete it->second;
261 delete pTables;
265 sal_uLong ScHTMLLayoutParser::Read( SvStream& rStream, const OUString& rBaseURL )
267 Link<> aOldLink = pEdit->GetImportHdl();
268 pEdit->SetImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
270 SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
271 bool bLoading = pObjSh && pObjSh->IsLoading();
273 SvKeyValueIteratorRef xValues;
274 SvKeyValueIterator* pAttributes = NULL;
275 if ( bLoading )
276 pAttributes = pObjSh->GetHeaderAttributes();
277 else
279 // When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
280 // (used when pasting from clipboard)
281 const sal_Char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
282 if( pCharSet )
284 OUString aContentType = "text/html; charset=";
285 aContentType += OUString::createFromAscii( pCharSet );
287 xValues = new SvKeyValueIterator;
288 xValues->Append( SvKeyValue( OUString( OOO_STRING_SVTOOLS_HTML_META_content_type ), aContentType ) );
289 pAttributes = xValues;
293 sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_HTML, pAttributes );
295 pEdit->SetImportHdl( aOldLink );
296 // Create column width
297 Adjust();
298 OutputDevice* pDefaultDev = Application::GetDefaultDevice();
299 sal_uInt16 nCount = pColOffset->size();
300 sal_uLong nOff = (*pColOffset)[0];
301 Size aSize;
302 for ( sal_uInt16 j = 1; j < nCount; j++ )
304 aSize.Width() = (*pColOffset)[j] - nOff;
305 aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MAP_TWIP ) );
306 maColWidths[ j-1 ] = aSize.Width();
307 nOff = (*pColOffset)[j];
309 return nErr;
312 const ScHTMLTable* ScHTMLLayoutParser::GetGlobalTable() const
314 return 0;
317 void ScHTMLLayoutParser::NewActEntry( ScEEParseEntry* pE )
319 ScEEParser::NewActEntry( pE );
320 if ( pE )
322 if ( !pE->aSel.HasRange() )
323 { // Completely empty, following text ends up in the same paragraph!
324 pActEntry->aSel.nStartPara = pE->aSel.nEndPara;
325 pActEntry->aSel.nStartPos = pE->aSel.nEndPos;
328 pActEntry->aSel.nEndPara = pActEntry->aSel.nStartPara;
329 pActEntry->aSel.nEndPos = pActEntry->aSel.nStartPos;
332 void ScHTMLLayoutParser::EntryEnd( ScEEParseEntry* pE, const ESelection& rSel )
334 if ( rSel.nEndPara >= pE->aSel.nStartPara )
336 pE->aSel.nEndPara = rSel.nEndPara;
337 pE->aSel.nEndPos = rSel.nEndPos;
339 else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
340 { // Did not attach a paragraph, but empty, do nothing
342 else
344 OSL_FAIL( "EntryEnd: EditEngine ESelection End < Start" );
348 void ScHTMLLayoutParser::NextRow( ImportInfo* pInfo )
350 if ( bInCell )
351 CloseEntry( pInfo );
352 if ( nRowMax < ++nRowCnt )
353 nRowMax = nRowCnt;
354 nColCnt = nColCntStart;
355 nColOffset = nColOffsetStart;
356 bFirstRow = false;
359 bool ScHTMLLayoutParser::SeekOffset( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
360 SCCOL* pCol, sal_uInt16 nOffsetTol )
362 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
363 ScHTMLColOffset::const_iterator it = pOffset->find( nOffset );
364 bool bFound = it != pOffset->end();
365 sal_uInt16 nPos = it - pOffset->begin();
366 *pCol = static_cast<SCCOL>(nPos);
367 if ( bFound )
368 return true;
369 sal_uInt16 nCount = pOffset->size();
370 if ( !nCount )
371 return false;
372 // nPos is the position of insertion, that's where the next higher one is (or isn't)
373 if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
374 return true;
375 // Not smaller than everything else? Then compare with the next lower one
376 else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
378 (*pCol)--;
379 return true;
381 return false;
384 void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
385 sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
387 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
388 SCCOL nPos;
389 if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
390 nOffset = (sal_uInt16)(*pOffset)[nPos];
391 else
392 pOffset->insert( nOffset );
393 if ( nWidth )
395 if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
396 nWidth = (sal_uInt16)(*pOffset)[nPos] - nOffset;
397 else
398 pOffset->insert( nOffset + nWidth );
402 void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
403 sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
405 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
406 SCCOL nPos;
407 if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
408 nOffset = (sal_uInt16)(*pOffset)[nPos];
409 else
410 pOffset->insert( nOffset );
411 if ( nWidth )
413 if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
414 pOffset->insert( nOffset + nWidth );
418 void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
419 sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
421 OSL_ENSURE( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
422 SCCOL nPos;
423 if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
425 if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
426 nNewOffset = (sal_uInt16)(*pOffset)[nPos];
427 else
428 pOffset->insert( nNewOffset );
429 return ;
431 nOldOffset = (sal_uInt16)(*pOffset)[nPos];
432 SCCOL nPos2;
433 if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
435 nNewOffset = (sal_uInt16)(*pOffset)[nPos2];
436 return ;
438 long nDiff = nNewOffset - nOldOffset;
439 if ( nDiff < 0 )
443 const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
444 } while ( nPos-- );
446 else
450 const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
451 } while ( ++nPos < (sal_uInt16)pOffset->size() );
455 void ScHTMLLayoutParser::SkipLocked( ScEEParseEntry* pE, bool bJoin )
457 if ( ValidCol(pE->nCol) )
458 { // Or else this would create a wrong value at ScAddress (chance for an infinite loop)!
459 bool bBadCol = false;
460 bool bAgain;
461 ScRange aRange( pE->nCol, pE->nRow, 0,
462 pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
465 bAgain = false;
466 for ( size_t i = 0, nRanges = xLockedList->size(); i < nRanges; ++i )
468 ScRange* pR = (*xLockedList)[i];
469 if ( pR->Intersects( aRange ) )
471 pE->nCol = pR->aEnd.Col() + 1;
472 SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
473 if ( pE->nCol > MAXCOL || nTmp > MAXCOL )
474 bBadCol = true;
475 else
477 bAgain = true;
478 aRange.aStart.SetCol( pE->nCol );
479 aRange.aEnd.SetCol( nTmp );
481 break;
484 } while ( bAgain );
485 if ( bJoin && !bBadCol )
486 xLockedList->Join( aRange );
490 void ScHTMLLayoutParser::Adjust()
492 xLockedList->RemoveAll();
494 ScHTMLAdjustStack aStack;
495 ScHTMLAdjustStackEntry* pS = NULL;
496 sal_uInt16 nTab = 0;
497 SCCOL nLastCol = SCCOL_MAX;
498 SCROW nNextRow = 0;
499 SCROW nCurRow = 0;
500 sal_uInt16 nPageWidth = (sal_uInt16) aPageSize.Width();
501 InnerMap* pTab = NULL;
502 for ( size_t i = 0, nListSize = maList.size(); i < nListSize; ++i )
504 ScEEParseEntry* pE = maList[ i ];
505 if ( pE->nTab < nTab )
506 { // Table finished
507 if ( !aStack.empty() )
509 pS = aStack.top();
510 aStack.pop();
512 nLastCol = pS->nLastCol;
513 nNextRow = pS->nNextRow;
514 nCurRow = pS->nCurRow;
516 delete pS;
517 pS = NULL;
518 nTab = pE->nTab;
519 if (pTables)
521 OuterMap::const_iterator it = pTables->find( nTab );
522 if ( it != pTables->end() )
523 pTab = it->second;
527 SCROW nRow = pE->nRow;
528 if ( pE->nCol <= nLastCol )
529 { // Next row
530 if ( pE->nRow < nNextRow )
531 pE->nRow = nCurRow = nNextRow;
532 else
533 nCurRow = nNextRow = pE->nRow;
534 SCROW nR = 0;
535 if ( pTab )
537 InnerMap::const_iterator it = pTab->find( nCurRow );
538 if ( it != pTab->end() )
539 nR = it->second;
541 if ( nR )
542 nNextRow += nR;
543 else
544 nNextRow++;
546 else
547 pE->nRow = nCurRow;
548 nLastCol = pE->nCol; // Read column
549 if ( pE->nTab > nTab )
550 { // New table
551 aStack.push( new ScHTMLAdjustStackEntry(
552 nLastCol, nNextRow, nCurRow ) );
553 nTab = pE->nTab;
554 if ( pTables )
556 OuterMap::const_iterator it = pTables->find( nTab );
557 if ( it != pTables->end() )
558 pTab = it->second;
560 // New line spacing
561 SCROW nR = 0;
562 if ( pTab )
564 InnerMap::const_iterator it = pTab->find( nCurRow );
565 if ( it != pTab->end() )
566 nR = it->second;
568 if ( nR )
569 nNextRow = nCurRow + nR;
570 else
571 nNextRow = nCurRow + 1;
573 if ( nTab == 0 )
574 pE->nWidth = nPageWidth;
575 else
576 { // Real table, no paragraphs on the field
577 if ( pTab )
579 SCROW nRowSpan = pE->nRowOverlap;
580 for ( SCROW j=0; j < nRowSpan; j++ )
581 { // RowSpan resulting from merged rows
582 SCROW nRows = 0;
583 InnerMap::const_iterator it = pTab->find( nRow+j );
584 if ( it != pTab->end() )
585 nRows = it->second;
586 if ( nRows > 1 )
588 pE->nRowOverlap += nRows - 1;
589 if ( j == 0 )
590 { // Merged rows move the next row
591 SCROW nTmp = nCurRow + nRows;
592 if ( nNextRow < nTmp )
593 nNextRow = nTmp;
599 // Real column
600 (void)SeekOffset( pColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
601 SCCOL nColBeforeSkip = pE->nCol;
602 SkipLocked( pE, false );
603 if ( pE->nCol != nColBeforeSkip )
605 SCCOL nCount = (SCCOL)pColOffset->size();
606 if ( nCount <= pE->nCol )
608 pE->nOffset = (sal_uInt16) (*pColOffset)[nCount-1];
609 MakeCol( pColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
611 else
613 pE->nOffset = (sal_uInt16) (*pColOffset)[pE->nCol];
616 SCCOL nPos;
617 if ( pE->nWidth && SeekOffset( pColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
618 pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
619 else
621 //FIXME: This may not be correct, but works anyway ...
622 pE->nColOverlap = 1;
624 xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
625 pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
626 // Take over MaxDimensions
627 SCCOL nColTmp = pE->nCol + pE->nColOverlap;
628 if ( nColMax < nColTmp )
629 nColMax = nColTmp;
630 SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
631 if ( nRowMax < nRowTmp )
632 nRowMax = nRowTmp;
634 while ( !aStack.empty() )
636 delete aStack.top();
637 aStack.pop();
641 sal_uInt16 ScHTMLLayoutParser::GetWidth( ScEEParseEntry* pE )
643 if ( pE->nWidth )
644 return pE->nWidth;
645 sal_Int32 nTmp = ::std::min( static_cast<sal_Int32>( pE->nCol -
646 nColCntStart + pE->nColOverlap),
647 static_cast<sal_Int32>( pLocalColOffset->size() - 1));
648 SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
649 sal_uInt16 nOff2 = (sal_uInt16) (*pLocalColOffset)[nPos];
650 if ( pE->nOffset < nOff2 )
651 return nOff2 - pE->nOffset;
652 return 0;
655 void ScHTMLLayoutParser::SetWidths()
657 ScEEParseEntry* pE;
658 SCCOL nCol;
659 if ( !nTableWidth )
660 nTableWidth = (sal_uInt16) aPageSize.Width();
661 SCCOL nColsPerRow = nMaxCol - nColCntStart;
662 if ( nColsPerRow <= 0 )
663 nColsPerRow = 1;
664 if ( pLocalColOffset->size() <= 2 )
665 { // Only PageSize, there was no width setting
666 sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
667 sal_uInt16 nOff = nColOffsetStart;
668 pLocalColOffset->clear();
669 for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
671 MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
673 nTableWidth = (sal_uInt16)(pLocalColOffset->back() - pLocalColOffset->front());
674 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
676 pE = maList[ i ];
677 if ( pE->nTab == nTable )
679 pE->nOffset = (sal_uInt16) (*pLocalColOffset)[pE->nCol - nColCntStart];
680 pE->nWidth = 0; // to be recalculated later
684 else
685 { // Some without width
686 // Why actually no pE?
687 if ( nFirstTableCell < maList.size() )
689 boost::scoped_array<sal_uInt16> pOffsets(new sal_uInt16[ nColsPerRow+1 ]);
690 memset( pOffsets.get(), 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
691 boost::scoped_array<sal_uInt16> pWidths(new sal_uInt16[ nColsPerRow ]);
692 memset( pWidths.get(), 0, nColsPerRow * sizeof(sal_uInt16) );
693 pOffsets[0] = nColOffsetStart;
694 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
696 pE = maList[ i ];
697 if ( pE->nTab == nTable && pE->nWidth )
699 nCol = pE->nCol - nColCntStart;
700 if ( nCol < nColsPerRow )
702 if ( pE->nColOverlap == 1 )
704 if ( pWidths[nCol] < pE->nWidth )
705 pWidths[nCol] = pE->nWidth;
707 else
708 { // try to find a single undefined width
709 sal_uInt16 nTotal = 0;
710 bool bFound = false;
711 SCCOL nHere = 0;
712 SCCOL nStop = std::min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
713 for ( ; nCol < nStop; nCol++ )
715 if ( pWidths[nCol] )
716 nTotal = nTotal + pWidths[nCol];
717 else
719 if ( bFound )
721 bFound = false;
722 break; // for
724 bFound = true;
725 nHere = nCol;
728 if ( bFound && pE->nWidth > nTotal )
729 pWidths[nHere] = pE->nWidth - nTotal;
734 sal_uInt16 nWidths = 0;
735 sal_uInt16 nUnknown = 0;
736 for ( nCol = 0; nCol < nColsPerRow; nCol++ )
738 if ( pWidths[nCol] )
739 nWidths = nWidths + pWidths[nCol];
740 else
741 nUnknown++;
743 if ( nUnknown )
745 sal_uInt16 nW = ((nWidths < nTableWidth) ?
746 ((nTableWidth - nWidths) / nUnknown) :
747 (nTableWidth / nUnknown));
748 for ( nCol = 0; nCol < nColsPerRow; nCol++ )
750 if ( !pWidths[nCol] )
751 pWidths[nCol] = nW;
754 for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
756 pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
758 pLocalColOffset->clear();
759 for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
761 MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
763 nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
765 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
767 pE = maList[ i ];
768 if ( pE->nTab == nTable )
770 nCol = pE->nCol - nColCntStart;
771 OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
772 if ( nCol < nColsPerRow )
774 pE->nOffset = pOffsets[nCol];
775 nCol = nCol + pE->nColOverlap;
776 if ( nCol > nColsPerRow )
777 nCol = nColsPerRow;
778 pE->nWidth = pOffsets[nCol] - pE->nOffset;
784 if ( !pLocalColOffset->empty() )
786 sal_uInt16 nMax = (sal_uInt16) pLocalColOffset->back();
787 if ( aPageSize.Width() < nMax )
788 aPageSize.Width() = nMax;
790 for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
792 pE = maList[ i ];
793 if ( pE->nTab == nTable )
795 if ( !pE->nWidth )
797 pE->nWidth = GetWidth( pE );
798 OSL_ENSURE( pE->nWidth, "SetWidths: pE->nWidth == 0" );
800 MakeCol( pColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
805 void ScHTMLLayoutParser::Colonize( ScEEParseEntry* pE )
807 if ( pE->nCol == SCCOL_MAX )
808 pE->nCol = nColCnt;
809 if ( pE->nRow == SCROW_MAX )
810 pE->nRow = nRowCnt;
811 SCCOL nCol = pE->nCol;
812 SkipLocked( pE ); // Change of columns to the right
814 if ( nCol < pE->nCol )
815 { // Replaced
816 nCol = pE->nCol - nColCntStart;
817 SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size());
818 if ( nCol < nCount )
819 nColOffset = (sal_uInt16) (*pLocalColOffset)[nCol];
820 else
821 nColOffset = (sal_uInt16) (*pLocalColOffset)[nCount - 1];
823 pE->nOffset = nColOffset;
824 sal_uInt16 nWidth = GetWidth( pE );
825 MakeCol( pLocalColOffset, pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance );
826 if ( pE->nWidth )
827 pE->nWidth = nWidth;
828 nColOffset = pE->nOffset + nWidth;
829 if ( nTableWidth < nColOffset - nColOffsetStart )
830 nTableWidth = nColOffset - nColOffsetStart;
833 void ScHTMLLayoutParser::CloseEntry( ImportInfo* pInfo )
835 bInCell = false;
836 if ( bTabInTabCell )
837 { // From the stack in TableOff
838 bTabInTabCell = false;
839 bool found = false;
840 for ( size_t i = 0, nListSize = maList.size(); i < nListSize; ++i )
842 if ( pActEntry == maList[ i ] )
844 found = true;
845 break;
848 if ( !found )
849 delete pActEntry;
850 NewActEntry( maList.back() ); // New free flying pActEntry
851 return ;
853 if ( pActEntry->nTab == 0 )
854 pActEntry->nWidth = (sal_uInt16) aPageSize.Width();
855 Colonize( pActEntry );
856 nColCnt = pActEntry->nCol + pActEntry->nColOverlap;
857 if ( nMaxCol < nColCnt )
858 nMaxCol = nColCnt; // TableStack MaxCol
859 if ( nColMax < nColCnt )
860 nColMax = nColCnt; // Global MaxCol for ScEEParser GetDimensions!
861 EntryEnd( pActEntry, pInfo->aSelection );
862 ESelection& rSel = pActEntry->aSel;
863 while ( rSel.nStartPara < rSel.nEndPara
864 && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
865 { // Strip preceding empty paragraphs
866 rSel.nStartPara++;
868 while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
869 { // Strip successive empty paragraphs
870 rSel.nEndPara--;
871 rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
873 if ( rSel.nStartPara > rSel.nEndPara )
874 { // Gives GPF in CreateTextObject
875 OSL_FAIL( "CloseEntry: EditEngine ESelection Start > End" );
876 rSel.nEndPara = rSel.nStartPara;
878 if ( rSel.HasRange() )
879 pActEntry->aItemSet.Put( SfxBoolItem( ATTR_LINEBREAK, true ) );
880 maList.push_back( pActEntry );
881 NewActEntry( pActEntry ); // New free flying pActEntry
884 IMPL_LINK( ScHTMLLayoutParser, HTMLImportHdl, ImportInfo*, pInfo )
886 switch ( pInfo->eState )
888 case HTMLIMP_NEXTTOKEN:
889 ProcToken( pInfo );
890 break;
891 case HTMLIMP_UNKNOWNATTR:
892 ProcToken( pInfo );
893 break;
894 case HTMLIMP_START:
895 break;
896 case HTMLIMP_END:
897 if ( pInfo->aSelection.nEndPos )
899 // If text remains: create paragraph, without calling CloseEntry().
900 if( bInCell ) // ...but only in opened table cells.
902 bInCell = false;
903 NextRow( pInfo );
904 bInCell = true;
906 CloseEntry( pInfo );
908 while ( nTableLevel > 0 )
909 TableOff( pInfo ); // close tables, if </TABLE> missing
910 break;
911 case HTMLIMP_SETATTR:
912 break;
913 case HTMLIMP_INSERTTEXT:
914 break;
915 case HTMLIMP_INSERTPARA:
916 if ( nTableLevel < 1 )
918 CloseEntry( pInfo );
919 NextRow( pInfo );
921 break;
922 case HTMLIMP_INSERTFIELD:
923 break;
924 default:
925 OSL_FAIL("HTMLImportHdl: unknown ImportInfo.eState");
927 return 0;
930 // Greatest common divisor (Euclid)
931 // Special case: 0 and something gives 1
932 static SCROW lcl_GGT( SCROW a, SCROW b )
934 if ( !a || !b )
935 return 1;
938 if ( a > b )
939 a -= SCROW(a / b) * b;
940 else
941 b -= SCROW(b / a) * a;
942 } while ( a && b );
943 return ((a != 0) ? a : b);
946 // Lowest common multiple: a * b / GCD(a,b)
947 static SCROW lcl_KGV( SCROW a, SCROW b )
949 if ( a > b ) // Make overflow even less likely
950 return (a / lcl_GGT(a,b)) * b;
951 else
952 return (b / lcl_GGT(a,b)) * a;
955 void ScHTMLLayoutParser::TableDataOn( ImportInfo* pInfo )
957 if ( bInCell )
958 CloseEntry( pInfo );
959 if ( !nTableLevel )
961 OSL_FAIL( "dumbo doc! <TH> or <TD> without previous <TABLE>" );
962 TableOn( pInfo );
964 bInCell = true;
965 bool bHorJustifyCenterTH = (pInfo->nToken == HTML_TABLEHEADER_ON);
966 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
967 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
969 const HTMLOption& rOption = rOptions[i];
970 switch( rOption.GetToken() )
972 case HTML_O_COLSPAN:
974 pActEntry->nColOverlap = ( SCCOL ) rOption.GetString().toInt32();
976 break;
977 case HTML_O_ROWSPAN:
979 pActEntry->nRowOverlap = ( SCROW ) rOption.GetString().toInt32();
981 break;
982 case HTML_O_ALIGN:
984 bHorJustifyCenterTH = false;
985 SvxCellHorJustify eVal;
986 const OUString& rOptVal = rOption.GetString();
987 if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
988 eVal = SVX_HOR_JUSTIFY_RIGHT;
989 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
990 eVal = SVX_HOR_JUSTIFY_CENTER;
991 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
992 eVal = SVX_HOR_JUSTIFY_LEFT;
993 else
994 eVal = SVX_HOR_JUSTIFY_STANDARD;
995 if ( eVal != SVX_HOR_JUSTIFY_STANDARD )
996 pActEntry->aItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY) );
998 break;
999 case HTML_O_VALIGN:
1001 SvxCellVerJustify eVal;
1002 const OUString& rOptVal = rOption.GetString();
1003 if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
1004 eVal = SVX_VER_JUSTIFY_TOP;
1005 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
1006 eVal = SVX_VER_JUSTIFY_CENTER;
1007 else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
1008 eVal = SVX_VER_JUSTIFY_BOTTOM;
1009 else
1010 eVal = SVX_VER_JUSTIFY_STANDARD;
1011 pActEntry->aItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY) );
1013 break;
1014 case HTML_O_WIDTH:
1016 pActEntry->nWidth = GetWidthPixel( rOption );
1018 break;
1019 case HTML_O_BGCOLOR:
1021 Color aColor;
1022 rOption.GetColor( aColor );
1023 pActEntry->aItemSet.Put(
1024 SvxBrushItem( aColor, ATTR_BACKGROUND ) );
1026 break;
1027 case HTML_O_SDVAL:
1029 pActEntry->pValStr = new OUString( rOption.GetString() );
1031 break;
1032 case HTML_O_SDNUM:
1034 pActEntry->pNumStr = new OUString( rOption.GetString() );
1036 break;
1039 pActEntry->nCol = nColCnt;
1040 pActEntry->nRow = nRowCnt;
1041 pActEntry->nTab = nTable;
1043 if ( bHorJustifyCenterTH )
1044 pActEntry->aItemSet.Put(
1045 SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY) );
1048 void ScHTMLLayoutParser::TableRowOn( ImportInfo* pInfo )
1050 if ( nColCnt > nColCntStart )
1051 NextRow( pInfo ); // The optional TableRowOff wasn't there
1052 nColOffset = nColOffsetStart;
1055 void ScHTMLLayoutParser::TableRowOff( ImportInfo* pInfo )
1057 NextRow( pInfo );
1060 void ScHTMLLayoutParser::TableDataOff( ImportInfo* pInfo )
1062 if ( bInCell )
1063 CloseEntry( pInfo ); // Only if it really was one
1066 void ScHTMLLayoutParser::TableOn( ImportInfo* pInfo )
1068 OUString aTabName;
1070 if ( ++nTableLevel > 1 )
1071 { // Table in Table
1072 sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize()
1073 Colonize( pActEntry );
1074 aTableStack.push( new ScHTMLTableStackEntry(
1075 pActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
1076 nColCnt, nRowCnt, nColCntStart, nMaxCol, nTable,
1077 nTableWidth, nColOffset, nColOffsetStart,
1078 bFirstRow ) );
1079 sal_uInt16 nLastWidth = nTableWidth;
1080 nTableWidth = GetWidth( pActEntry );
1081 if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
1082 { // There must be more than one, so this one cannot be enough
1083 nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
1085 nLastWidth = nTableWidth;
1086 if ( pInfo->nToken == HTML_TABLE_ON )
1087 { // It can still be TD or TH, if we didn't have a TABLE earlier
1088 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1089 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
1091 const HTMLOption& rOption = rOptions[i];
1092 switch( rOption.GetToken() )
1094 case HTML_O_WIDTH:
1095 { // Percent: of document width or outer cell
1096 nTableWidth = GetWidthPixel( rOption );
1098 break;
1099 case HTML_O_BORDER:
1100 // Border is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1101 break;
1102 case HTML_O_ID:
1103 aTabName = rOption.GetString();
1104 break;
1108 bInCell = false;
1109 if ( bTabInTabCell && !(nTableWidth < nLastWidth) )
1110 { // Multiple tables in one cell, underneath each other
1111 bTabInTabCell = false;
1112 NextRow( pInfo );
1114 else
1115 { // It start's in this cell or next to each other
1116 bTabInTabCell = false;
1117 nColCntStart = nColCnt;
1118 nColOffset = nTmpColOffset;
1119 nColOffsetStart = nColOffset;
1122 ScEEParseEntry* pE = NULL;
1123 if (maList.size())
1124 pE = maList.back();
1125 NewActEntry( pE ); // New free flying pActEntry
1126 xLockedList = new ScRangeList;
1128 else
1129 { // Simple table at the document level
1130 EntryEnd( pActEntry, pInfo->aSelection );
1131 if ( pActEntry->aSel.HasRange() )
1132 { // Flying text left
1133 CloseEntry( pInfo );
1134 NextRow( pInfo );
1136 aTableStack.push( new ScHTMLTableStackEntry(
1137 pActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
1138 nColCnt, nRowCnt, nColCntStart, nMaxCol, nTable,
1139 nTableWidth, nColOffset, nColOffsetStart,
1140 bFirstRow ) );
1141 // As soon as we have multiple tables we need to be tolerant with the offsets.
1142 if (nMaxTable > 0)
1143 nOffsetTolerance = SC_HTML_OFFSET_TOLERANCE_LARGE;
1144 nTableWidth = 0;
1145 if ( pInfo->nToken == HTML_TABLE_ON )
1147 // It can still be TD or TH, if we didn't have a TABLE earlier
1148 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1149 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
1151 const HTMLOption& rOption = rOptions[i];
1152 switch( rOption.GetToken() )
1154 case HTML_O_WIDTH:
1155 { // Percent: of document width or outer cell
1156 nTableWidth = GetWidthPixel( rOption );
1158 break;
1159 case HTML_O_BORDER:
1160 //BorderOn is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1161 break;
1162 case HTML_O_ID:
1163 aTabName = rOption.GetString();
1164 break;
1169 nTable = ++nMaxTable;
1170 bFirstRow = true;
1171 nFirstTableCell = maList.size();
1173 pLocalColOffset = new ScHTMLColOffset;
1174 MakeColNoRef( pLocalColOffset, nColOffsetStart, 0, 0, 0 );
1177 void ScHTMLLayoutParser::TableOff( ImportInfo* pInfo )
1179 if ( bInCell )
1180 CloseEntry( pInfo );
1181 if ( nColCnt > nColCntStart )
1182 TableRowOff( pInfo ); // The optional TableRowOff wasn't
1183 if ( !nTableLevel )
1185 OSL_FAIL( "dumbo doc! </TABLE> without opening <TABLE>" );
1186 return ;
1188 if ( --nTableLevel > 0 )
1189 { // Table in Table done
1190 if ( !aTableStack.empty() )
1192 ScHTMLTableStackEntry* pS = aTableStack.top();
1193 aTableStack.pop();
1195 ScEEParseEntry* pE = pS->pCellEntry;
1196 SCROW nRows = nRowCnt - pS->nRowCnt;
1197 if ( nRows > 1 )
1198 { // Insert size of table at this position
1199 SCROW nRow = pS->nRowCnt;
1200 sal_uInt16 nTab = pS->nTable;
1201 if ( !pTables )
1202 pTables = new OuterMap;
1203 // Height of outer table
1204 OuterMap::const_iterator it = pTables->find( nTab );
1205 InnerMap* pTab1;
1206 if ( it == pTables->end() )
1208 pTab1 = new InnerMap;
1209 (*pTables)[ nTab ] = pTab1;
1211 else
1212 pTab1 = it->second;
1213 SCROW nRowSpan = pE->nRowOverlap;
1214 SCROW nRowKGV;
1215 SCROW nRowsPerRow1; // Outer table
1216 SCROW nRowsPerRow2; // Inner table
1217 if ( nRowSpan > 1 )
1218 { // LCM to which we can map the inner and outer rows
1219 nRowKGV = lcl_KGV( nRowSpan, nRows );
1220 nRowsPerRow1 = nRowKGV / nRowSpan;
1221 nRowsPerRow2 = nRowKGV / nRows;
1223 else
1225 nRowKGV = nRowsPerRow1 = nRows;
1226 nRowsPerRow2 = 1;
1228 InnerMap* pTab2 = NULL;
1229 if ( nRowsPerRow2 > 1 )
1230 { // Height of the inner table
1231 pTab2 = new InnerMap;
1232 (*pTables)[ nTable ] = pTab2;
1234 // Abuse void* Data entry of the Table class for height mapping
1235 if ( nRowKGV > 1 )
1237 if ( nRowsPerRow1 > 1 )
1238 { // Outer
1239 for ( SCROW j=0; j < nRowSpan; j++ )
1241 sal_uLong nRowKey = nRow + j;
1242 SCROW nR = (*pTab1)[ nRowKey ];
1243 if ( !nR )
1244 (*pTab1)[ nRowKey ] = nRowsPerRow1;
1245 else if ( nRowsPerRow1 > nR )
1246 (*pTab1)[ nRowKey ] = nRowsPerRow1;
1247 //TODO: How can we improve on this?
1248 else if ( nRowsPerRow1 < nR && nRowSpan == 1
1249 && nTable == nMaxTable )
1250 { // Still some space left, merge in a better way (if possible)
1251 SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
1252 nR += nAdd;
1253 if ( (nR % nRows) == 0 )
1254 { // Only if representable
1255 SCROW nR2 = (*pTab1)[ nRowKey+1 ];
1256 if ( nR2 > nAdd )
1257 { // Only if we really have enough space
1258 (*pTab1)[ nRowKey ] = nR;
1259 (*pTab1)[ nRowKey+1 ] = nR2 - nAdd;
1260 nRowsPerRow2 = nR / nRows;
1266 if ( nRowsPerRow2 > 1 )
1267 { // Inner
1268 if ( !pTab2 )
1269 { // nRowsPerRow2 could be've been incremented
1270 pTab2 = new InnerMap;
1271 (*pTables)[ nTable ] = pTab2;
1273 for ( SCROW j=0; j < nRows; j++ )
1275 sal_uLong nRowKey = nRow + j;
1276 (*pTab2)[ nRowKey ] = nRowsPerRow2;
1282 SetWidths();
1284 if ( !pE->nWidth )
1285 pE->nWidth = nTableWidth;
1286 else if ( pE->nWidth < nTableWidth )
1288 sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
1289 sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
1290 ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
1291 sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
1292 pE->nWidth = nNewOffset - pE->nOffset;
1293 pS->nTableWidth = pS->nTableWidth + nTmp;
1294 if ( pS->nColOffset >= nOldOffset )
1295 pS->nColOffset = pS->nColOffset + nTmp;
1298 nColCnt = pE->nCol + pE->nColOverlap;
1299 nRowCnt = pS->nRowCnt;
1300 nColCntStart = pS->nColCntStart;
1301 nMaxCol = pS->nMaxCol;
1302 nTable = pS->nTable;
1303 nTableWidth = pS->nTableWidth;
1304 nFirstTableCell = pS->nFirstTableCell;
1305 nColOffset = pS->nColOffset;
1306 nColOffsetStart = pS->nColOffsetStart;
1307 bFirstRow = pS->bFirstRow;
1308 xLockedList = pS->xLockedList;
1309 delete pLocalColOffset;
1310 pLocalColOffset = pS->pLocalColOffset;
1311 delete pActEntry;
1312 // pActEntry is kept around if a table is started in the same row
1313 // (anything's possible in HTML); will be deleted by CloseEntry
1314 pActEntry = pE;
1315 delete pS;
1317 bTabInTabCell = true;
1318 bInCell = true;
1320 else
1321 { // Simple table finished
1322 SetWidths();
1323 nMaxCol = 0;
1324 nTable = 0;
1325 if ( !aTableStack.empty() )
1327 ScHTMLTableStackEntry* pS = aTableStack.top();
1328 aTableStack.pop();
1329 delete pLocalColOffset;
1330 pLocalColOffset = pS->pLocalColOffset;
1331 delete pS;
1336 void ScHTMLLayoutParser::Image( ImportInfo* pInfo )
1338 ScHTMLImage* pImage = new ScHTMLImage;
1339 pActEntry->maImageList.push_back( pImage );
1340 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1341 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
1343 const HTMLOption& rOption = rOptions[i];
1344 switch( rOption.GetToken() )
1346 case HTML_O_SRC:
1348 pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, rOption.GetString() );
1350 break;
1351 case HTML_O_ALT:
1353 if ( !pActEntry->bHasGraphic )
1354 { // ALT text only if not any image loaded
1355 if (!pActEntry->aAltText.isEmpty())
1356 pActEntry->aAltText += "; ";
1358 pActEntry->aAltText += rOption.GetString();
1361 break;
1362 case HTML_O_WIDTH:
1364 pImage->aSize.Width() = (long)rOption.GetNumber();
1366 break;
1367 case HTML_O_HEIGHT:
1369 pImage->aSize.Height() = (long)rOption.GetNumber();
1371 break;
1372 case HTML_O_HSPACE:
1374 pImage->aSpace.X() = (long)rOption.GetNumber();
1376 break;
1377 case HTML_O_VSPACE:
1379 pImage->aSpace.Y() = (long)rOption.GetNumber();
1381 break;
1384 if (pImage->aURL.isEmpty())
1386 OSL_FAIL( "Image: graphic without URL ?!?" );
1387 return ;
1390 sal_uInt16 nFormat;
1391 Graphic* pGraphic = new Graphic;
1392 GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
1393 if ( GRFILTER_OK != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
1394 *pGraphic, &rFilter, &nFormat ) )
1396 delete pGraphic;
1397 return ; // Bad luck
1399 if ( !pActEntry->bHasGraphic )
1400 { // discard any ALT text in this cell if we have any image
1401 pActEntry->bHasGraphic = true;
1402 (pActEntry->aAltText).clear();
1404 pImage->aFilterName = rFilter.GetImportFormatName( nFormat );
1405 pImage->pGraphic = pGraphic;
1406 if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
1408 OutputDevice* pDefaultDev = Application::GetDefaultDevice();
1409 pImage->aSize = pDefaultDev->LogicToPixel( pGraphic->GetPrefSize(),
1410 pGraphic->GetPrefMapMode() );
1412 if ( pActEntry->maImageList.size() > 0 )
1414 long nWidth = 0;
1415 for ( sal_uInt32 i=0; i < pActEntry->maImageList.size(); ++i )
1417 ScHTMLImage* pI = &pActEntry->maImageList[ i ];
1418 if ( pI->nDir & nHorizontal )
1419 nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
1420 else
1421 nWidth = 0;
1423 if ( pActEntry->nWidth
1424 && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
1425 >= pActEntry->nWidth) )
1426 pActEntry->maImageList.back().nDir = nVertical;
1430 void ScHTMLLayoutParser::ColOn( ImportInfo* pInfo )
1432 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1433 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
1435 const HTMLOption& rOption = rOptions[i];
1436 switch( rOption.GetToken() )
1438 case HTML_O_WIDTH:
1440 sal_uInt16 nVal = GetWidthPixel( rOption );
1441 MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
1442 nColOffset = nColOffset + nVal;
1444 break;
1449 sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption& rOption )
1451 const OUString& rOptVal = rOption.GetString();
1452 if ( rOptVal.indexOf('%') != -1 )
1453 { // Percent
1454 sal_uInt16 nW = (nTableWidth ? nTableWidth : (sal_uInt16) aPageSize.Width());
1455 return (sal_uInt16)((rOption.GetNumber() * nW) / 100);
1457 else
1459 if ( rOptVal.indexOf('*') != -1 )
1460 { // Relative to what?
1461 // TODO: Collect all relative values in ColArray and then MakeCol
1462 return 0;
1464 else
1465 return (sal_uInt16)rOption.GetNumber(); // Pixel
1469 void ScHTMLLayoutParser::AnchorOn( ImportInfo* pInfo )
1471 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1472 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
1474 const HTMLOption& rOption = rOptions[i];
1475 switch( rOption.GetToken() )
1477 case HTML_O_NAME:
1479 pActEntry->pName = new OUString(rOption.GetString());
1481 break;
1486 bool ScHTMLLayoutParser::IsAtBeginningOfText( ImportInfo* pInfo )
1488 ESelection& rSel = pActEntry->aSel;
1489 return rSel.nStartPara == rSel.nEndPara &&
1490 rSel.nStartPara <= pInfo->aSelection.nEndPara &&
1491 pEdit->GetTextLen( rSel.nStartPara ) == 0;
1494 void ScHTMLLayoutParser::FontOn( ImportInfo* pInfo )
1496 if ( IsAtBeginningOfText( pInfo ) )
1497 { // Only at the start of the text; applies to whole line
1498 const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
1499 for (size_t i = 0, n = rOptions.size(); i < n; ++i)
1501 const HTMLOption& rOption = rOptions[i];
1502 switch( rOption.GetToken() )
1504 case HTML_O_FACE :
1506 const OUString& rFace = rOption.GetString();
1507 OUString aFontName;
1508 sal_Int32 nPos = 0;
1509 while( nPos != -1 )
1511 // Font list, VCL uses the semicolon as separator
1512 // HTML uses the comma
1513 OUString aFName = rFace.getToken( 0, ',', nPos );
1514 aFName = comphelper::string::strip(aFName, ' ');
1515 if( !aFontName.isEmpty() )
1516 aFontName += ";";
1517 aFontName += aFName;
1519 if ( !aFontName.isEmpty() )
1520 pActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
1521 aFontName, EMPTY_OUSTRING, PITCH_DONTKNOW,
1522 RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
1524 break;
1525 case HTML_O_SIZE :
1527 sal_uInt16 nSize = (sal_uInt16) rOption.GetNumber();
1528 if ( nSize == 0 )
1529 nSize = 1;
1530 else if ( nSize > SC_HTML_FONTSIZES )
1531 nSize = SC_HTML_FONTSIZES;
1532 pActEntry->aItemSet.Put( SvxFontHeightItem(
1533 maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
1535 break;
1536 case HTML_O_COLOR :
1538 Color aColor;
1539 rOption.GetColor( aColor );
1540 pActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
1542 break;
1548 void ScHTMLLayoutParser::ProcToken( ImportInfo* pInfo )
1550 bool bSetLastToken = true;
1551 switch ( pInfo->nToken )
1553 case HTML_META:
1555 HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser);
1556 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1557 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
1558 pParser->ParseMetaOptions(
1559 xDPS->getDocumentProperties(),
1560 mpDoc->GetDocumentShell()->GetHeaderAttributes() );
1562 break;
1563 case HTML_TITLE_ON:
1565 bInTitle = true;
1566 aString.clear();
1568 break;
1569 case HTML_TITLE_OFF:
1571 if ( bInTitle && !aString.isEmpty() )
1573 // Remove blanks from line brakes
1574 aString = aString.trim();
1575 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1576 mpDoc->GetDocumentShell()->GetModel(),
1577 uno::UNO_QUERY_THROW);
1578 xDPS->getDocumentProperties()->setTitle(aString);
1580 bInTitle = false;
1582 break;
1583 case HTML_TABLE_ON:
1585 TableOn( pInfo );
1587 break;
1588 case HTML_COL_ON:
1590 ColOn( pInfo );
1592 break;
1593 case HTML_TABLEHEADER_ON: // Opens row
1595 if ( bInCell )
1596 CloseEntry( pInfo );
1597 // Do not set bInCell to true, TableDataOn does that
1598 pActEntry->aItemSet.Put(
1599 SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT) );
1600 } // fall through
1601 case HTML_TABLEDATA_ON: // Opens cell
1603 TableDataOn( pInfo );
1605 break;
1606 case HTML_TABLEHEADER_OFF:
1607 case HTML_TABLEDATA_OFF: // Closes cell
1609 TableDataOff( pInfo );
1611 break;
1612 case HTML_TABLEROW_ON: // Before first cell in row
1614 TableRowOn( pInfo );
1616 break;
1617 case HTML_TABLEROW_OFF: // After last cell in row
1619 TableRowOff( pInfo );
1621 break;
1622 case HTML_TABLE_OFF:
1624 TableOff( pInfo );
1626 break;
1627 case HTML_IMAGE:
1629 Image( pInfo );
1631 break;
1632 case HTML_PARABREAK_OFF:
1633 { // We continue vertically after an image
1634 if ( pActEntry->maImageList.size() > 0 )
1635 pActEntry->maImageList.back().nDir = nVertical;
1637 break;
1638 case HTML_ANCHOR_ON:
1640 AnchorOn( pInfo );
1642 break;
1643 case HTML_FONT_ON :
1645 FontOn( pInfo );
1647 break;
1648 case HTML_BIGPRINT_ON :
1650 // TODO: Remember current font size and increase by 1
1651 if ( IsAtBeginningOfText( pInfo ) )
1652 pActEntry->aItemSet.Put( SvxFontHeightItem(
1653 maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
1655 break;
1656 case HTML_SMALLPRINT_ON :
1658 // TODO: Remember current font size and decrease by 1
1659 if ( IsAtBeginningOfText( pInfo ) )
1660 pActEntry->aItemSet.Put( SvxFontHeightItem(
1661 maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
1663 break;
1664 case HTML_BOLD_ON :
1665 case HTML_STRONG_ON :
1667 if ( IsAtBeginningOfText( pInfo ) )
1668 pActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1669 ATTR_FONT_WEIGHT ) );
1671 break;
1672 case HTML_ITALIC_ON :
1673 case HTML_EMPHASIS_ON :
1674 case HTML_ADDRESS_ON :
1675 case HTML_BLOCKQUOTE_ON :
1676 case HTML_BLOCKQUOTE30_ON :
1677 case HTML_CITIATION_ON :
1678 case HTML_VARIABLE_ON :
1680 if ( IsAtBeginningOfText( pInfo ) )
1681 pActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1682 ATTR_FONT_POSTURE ) );
1684 break;
1685 case HTML_DEFINSTANCE_ON :
1687 if ( IsAtBeginningOfText( pInfo ) )
1689 pActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1690 ATTR_FONT_WEIGHT ) );
1691 pActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1692 ATTR_FONT_POSTURE ) );
1695 break;
1696 case HTML_UNDERLINE_ON :
1698 if ( IsAtBeginningOfText( pInfo ) )
1699 pActEntry->aItemSet.Put( SvxUnderlineItem( UNDERLINE_SINGLE,
1700 ATTR_FONT_UNDERLINE ) );
1702 break;
1703 case HTML_TEXTTOKEN:
1705 if ( bInTitle )
1706 aString += pInfo->aText;
1708 break;
1709 default:
1710 { // Don't set nLastToken!
1711 bSetLastToken = false;
1714 if ( bSetLastToken )
1715 nLastToken = pInfo->nToken;
1718 // HTML DATA QUERY PARSER
1720 template< typename Type >
1721 inline Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
1722 { return ::std::max( ::std::min( rValue, rMax ), rMin ); }
1724 ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
1725 ScEEParseEntry( rItemSet ),
1726 mbImportAlways( false )
1728 nTab = nTableId;
1729 bEntirePara = false;
1732 bool ScHTMLEntry::HasContents() const
1734 return mbImportAlways || aSel.HasRange() || !aAltText.isEmpty() || IsTable();
1737 void ScHTMLEntry::AdjustStart( const ImportInfo& rInfo )
1739 // set start position
1740 aSel.nStartPara = rInfo.aSelection.nStartPara;
1741 aSel.nStartPos = rInfo.aSelection.nStartPos;
1742 // adjust end position
1743 if( (aSel.nEndPara < aSel.nStartPara) || ((aSel.nEndPara == aSel.nStartPara) && (aSel.nEndPos < aSel.nStartPos)) )
1745 aSel.nEndPara = aSel.nStartPara;
1746 aSel.nEndPos = aSel.nStartPos;
1750 void ScHTMLEntry::AdjustEnd( const ImportInfo& rInfo )
1752 OSL_ENSURE( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
1753 ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
1754 "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
1755 // set end position
1756 aSel.nEndPara = rInfo.aSelection.nEndPara;
1757 aSel.nEndPos = rInfo.aSelection.nEndPos;
1760 void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
1762 // strip leading empty paragraphs
1763 while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
1765 ++aSel.nStartPara;
1766 aSel.nStartPos = 0;
1768 // strip trailing empty paragraphs
1769 while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
1771 --aSel.nEndPara;
1772 aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
1776 /** A map of ScHTMLTable objects.
1778 Organizes the tables with a unique table key. Stores nested tables inside
1779 the parent table and forms in this way a tree structure of tables. An
1780 instance of this class ownes the contained table objects and deletes them
1781 on destruction.
1783 class ScHTMLTableMap
1785 private:
1786 typedef ::boost::shared_ptr< ScHTMLTable > ScHTMLTablePtr;
1787 typedef ::std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
1789 public:
1790 typedef ScHTMLTableStdMap::iterator iterator;
1791 typedef ScHTMLTableStdMap::const_iterator const_iterator;
1793 private:
1794 ScHTMLTable& mrParentTable; /// Reference to parent table.
1795 ScHTMLTableStdMap maTables; /// Container for all table objects.
1796 mutable ScHTMLTable* mpCurrTable; /// Current table, used for fast search.
1798 public:
1799 explicit ScHTMLTableMap( ScHTMLTable& rParentTable );
1800 virtual ~ScHTMLTableMap();
1802 inline const_iterator begin() const { return maTables.begin(); }
1803 inline const_iterator end() const { return maTables.end(); }
1805 /** Returns the specified table.
1806 @param nTableId Unique identifier of the table.
1807 @param bDeep true = searches deep in all nested table; false = only in this container. */
1808 ScHTMLTable* FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
1810 /** Inserts a new table into the container. This container owns the created table.
1811 @param bPreFormText true = New table is based on preformatted text (<pre> tag). */
1812 ScHTMLTable* CreateTable( const ImportInfo& rInfo, bool bPreFormText );
1814 private:
1815 /** Sets a working table with its index for search optimization. */
1816 inline void SetCurrTable( ScHTMLTable* pTable ) const
1817 { if( pTable ) mpCurrTable = pTable; }
1820 ScHTMLTableMap::ScHTMLTableMap( ScHTMLTable& rParentTable ) :
1821 mrParentTable(rParentTable),
1822 mpCurrTable(NULL)
1826 ScHTMLTableMap::~ScHTMLTableMap()
1830 ScHTMLTable* ScHTMLTableMap::FindTable( ScHTMLTableId nTableId, bool bDeep ) const
1832 ScHTMLTable* pResult = 0;
1833 if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
1834 pResult = mpCurrTable; // cached table
1835 else
1837 const_iterator aFind = maTables.find( nTableId );
1838 if( aFind != maTables.end() )
1839 pResult = aFind->second.get(); // table from this container
1842 // not found -> search deep in nested tables
1843 if( !pResult && bDeep )
1844 for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
1845 pResult = aIter->second->FindNestedTable( nTableId );
1847 SetCurrTable( pResult );
1848 return pResult;
1851 ScHTMLTable* ScHTMLTableMap::CreateTable( const ImportInfo& rInfo, bool bPreFormText )
1853 ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText );
1854 maTables[ pTable->GetTableId() ].reset( pTable );
1855 SetCurrTable( pTable );
1856 return pTable;
1859 /** Simplified forward iterator for convenience.
1861 Before the iterator can be dereferenced, it must be tested with the is()
1862 method. The iterator may be invalid directly after construction (e.g. empty
1863 container).
1865 class ScHTMLTableIterator
1867 public:
1868 /** Constructs the iterator for the passed table map.
1869 @param pTableMap Pointer to the table map (is allowed to be NULL). */
1870 explicit ScHTMLTableIterator( const ScHTMLTableMap* pTableMap );
1872 inline bool is() const { return mpTableMap && maIter != maEnd; }
1873 inline ScHTMLTable* operator->() { return maIter->second.get(); }
1874 inline ScHTMLTableIterator& operator++() { ++maIter; return *this; }
1876 private:
1877 ScHTMLTableMap::const_iterator maIter;
1878 ScHTMLTableMap::const_iterator maEnd;
1879 const ScHTMLTableMap* mpTableMap;
1882 ScHTMLTableIterator::ScHTMLTableIterator( const ScHTMLTableMap* pTableMap ) :
1883 mpTableMap(pTableMap)
1885 if( pTableMap )
1887 maIter = pTableMap->begin();
1888 maEnd = pTableMap->end();
1892 ScHTMLTableAutoId::ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId ) :
1893 mnTableId( rnUnusedId ),
1894 mrnUnusedId( rnUnusedId )
1896 ++mrnUnusedId;
1899 ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const ImportInfo& rInfo, bool bPreFormText ) :
1900 mpParentTable( &rParentTable ),
1901 maTableId( rParentTable.maTableId.mrnUnusedId ),
1902 maTableItemSet( rParentTable.GetCurrItemSet() ),
1903 mrEditEngine( rParentTable.mrEditEngine ),
1904 mrEEParseList( rParentTable.mrEEParseList ),
1905 mpCurrEntryList( 0 ),
1906 maSize( 1, 1 ),
1907 mpParser(rParentTable.mpParser),
1908 mbBorderOn( false ),
1909 mbPreFormText( bPreFormText ),
1910 mbRowOn( false ),
1911 mbDataOn( false ),
1912 mbPushEmptyLine( false )
1914 if( mbPreFormText )
1916 ImplRowOn();
1917 ImplDataOn( ScHTMLSize( 1, 1 ) );
1919 else
1921 ProcessFormatOptions( maTableItemSet, rInfo );
1922 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
1923 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
1924 for (; itr != itrEnd; ++itr)
1926 switch( itr->GetToken() )
1928 case HTML_O_BORDER:
1929 mbBorderOn = itr->GetString().isEmpty() || (itr->GetNumber() != 0);
1930 break;
1931 case HTML_O_ID:
1932 maTableName = itr->GetString();
1933 break;
1938 CreateNewEntry( rInfo );
1941 ScHTMLTable::ScHTMLTable(
1942 SfxItemPool& rPool,
1943 EditEngine& rEditEngine,
1944 ::std::vector< ScEEParseEntry* >& rEEParseList,
1945 ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser
1947 mpParentTable( 0 ),
1948 maTableId( rnUnusedId ),
1949 maTableItemSet( rPool ),
1950 mrEditEngine( rEditEngine ),
1951 mrEEParseList( rEEParseList ),
1952 mpCurrEntryList( 0 ),
1953 maSize( 1, 1 ),
1954 mpParser(pParser),
1955 mbBorderOn( false ),
1956 mbPreFormText( false ),
1957 mbRowOn( false ),
1958 mbDataOn( false ),
1959 mbPushEmptyLine( false )
1961 // open the first "cell" of the document
1962 ImplRowOn();
1963 ImplDataOn( ScHTMLSize( 1, 1 ) );
1964 mxCurrEntry = CreateEntry();
1967 ScHTMLTable::~ScHTMLTable()
1971 const SfxItemSet& ScHTMLTable::GetCurrItemSet() const
1973 // first try cell item set, then row item set, then table item set
1974 return mxDataItemSet.get() ? *mxDataItemSet : (mxRowItemSet.get() ? *mxRowItemSet : maTableItemSet);
1977 ScHTMLSize ScHTMLTable::GetSpan( const ScHTMLPos& rCellPos ) const
1979 ScHTMLSize aSpan( 1, 1 );
1980 const ScRange* pRange = NULL;
1981 if( ( (pRange = maVMergedCells.Find( rCellPos.MakeAddr() ) ) != 0)
1982 || ( (pRange = maHMergedCells.Find( rCellPos.MakeAddr() ) ) != 0)
1984 aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
1985 return aSpan;
1988 ScHTMLTable* ScHTMLTable::FindNestedTable( ScHTMLTableId nTableId ) const
1990 return mxNestedTables.get() ? mxNestedTables->FindTable( nTableId, true ) : 0;
1993 void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
1995 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::PutItem - no current entry" );
1996 if( mxCurrEntry.get() && mxCurrEntry->IsEmpty() )
1997 mxCurrEntry->GetItemSet().Put( rItem );
2000 void ScHTMLTable::PutText( const ImportInfo& rInfo )
2002 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::PutText - no current entry" );
2003 if( mxCurrEntry.get() )
2005 if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
2006 mxCurrEntry->AdjustStart( rInfo );
2007 else
2008 mxCurrEntry->AdjustEnd( rInfo );
2012 void ScHTMLTable::InsertPara( const ImportInfo& rInfo )
2014 if( mxCurrEntry.get() && mbDataOn && !IsEmptyCell() )
2015 mxCurrEntry->SetImportAlways();
2016 PushEntry( rInfo );
2017 CreateNewEntry( rInfo );
2018 InsertLeadingEmptyLine();
2021 void ScHTMLTable::BreakOn()
2023 // empty line, if <br> is at start of cell
2024 mbPushEmptyLine = !mbPreFormText && mbDataOn && IsEmptyCell();
2027 void ScHTMLTable::HeadingOn()
2029 // call directly, InsertPara() has not been called before
2030 InsertLeadingEmptyLine();
2033 void ScHTMLTable::InsertLeadingEmptyLine()
2035 // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
2036 mbPushEmptyLine = !mbPreFormText && mbDataOn && !IsEmptyCell();
2039 void ScHTMLTable::AnchorOn()
2041 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::AnchorOn - no current entry" );
2042 // don't skip entries with single hyperlinks
2043 if( mxCurrEntry.get() )
2044 mxCurrEntry->SetImportAlways();
2047 ScHTMLTable* ScHTMLTable::TableOn( const ImportInfo& rInfo )
2049 PushEntry( rInfo );
2050 return InsertNestedTable( rInfo, false );
2053 ScHTMLTable* ScHTMLTable::TableOff( const ImportInfo& rInfo )
2055 return mbPreFormText ? this : CloseTable( rInfo );
2058 ScHTMLTable* ScHTMLTable::PreOn( const ImportInfo& rInfo )
2060 PushEntry( rInfo );
2061 return InsertNestedTable( rInfo, true );
2064 ScHTMLTable* ScHTMLTable::PreOff( const ImportInfo& rInfo )
2066 return mbPreFormText ? CloseTable( rInfo ) : this;
2069 void ScHTMLTable::RowOn( const ImportInfo& rInfo )
2071 PushEntry( rInfo, true );
2072 if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
2074 ImplRowOn();
2075 ProcessFormatOptions( *mxRowItemSet, rInfo );
2077 CreateNewEntry( rInfo );
2080 void ScHTMLTable::RowOff( const ImportInfo& rInfo )
2082 PushEntry( rInfo, true );
2083 if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
2084 ImplRowOff();
2085 CreateNewEntry( rInfo );
2088 namespace {
2091 * Decode a numbert format string stored in Excel-generated HTML's CSS
2092 * region.
2094 OUString decodeNumberFormat(const OUString& rFmt)
2096 OUStringBuffer aBuf;
2097 const sal_Unicode* p = rFmt.getStr();
2098 sal_Int32 n = rFmt.getLength();
2099 for (sal_Int32 i = 0; i < n; ++i, ++p)
2101 if (*p == '\\')
2103 // Skip '\'.
2104 ++i;
2105 ++p;
2107 // Parse all subsequent digits until first non-digit is found.
2108 sal_Int32 nDigitCount = 0;
2109 const sal_Unicode* p1 = p;
2110 for (; i < n; ++i, ++p, ++nDigitCount)
2112 if (*p < '0' || '9' < *p)
2114 --i;
2115 --p;
2116 break;
2120 if (nDigitCount)
2122 // Hex-encoded character found. Decode it back into its
2123 // original character. An example of number format with
2124 // hex-encoded chars: "\0022$\0022\#\,\#\#0\.00"
2125 sal_uInt32 nVal = OUString(p1, nDigitCount).toUInt32(16);
2126 aBuf.append(static_cast<sal_Unicode>(nVal));
2129 else
2130 aBuf.append(*p);
2132 return aBuf.makeStringAndClear();
2137 void ScHTMLTable::DataOn( const ImportInfo& rInfo )
2139 PushEntry( rInfo, true );
2140 if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
2142 // read needed options from the <td> tag
2143 ScHTMLSize aSpanSize( 1, 1 );
2144 ::std::unique_ptr<OUString> pValStr, pNumStr;
2145 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2146 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
2147 sal_uInt32 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
2148 for (; itr != itrEnd; ++itr)
2150 switch (itr->GetToken())
2152 case HTML_O_COLSPAN:
2153 aSpanSize.mnCols = static_cast<SCCOL>( getLimitedValue<sal_Int32>( itr->GetString().toInt32(), 1, 256 ) );
2154 break;
2155 case HTML_O_ROWSPAN:
2156 aSpanSize.mnRows = static_cast<SCROW>( getLimitedValue<sal_Int32>( itr->GetString().toInt32(), 1, 256 ) );
2157 break;
2158 case HTML_O_SDVAL:
2159 pValStr.reset(new OUString(itr->GetString()));
2160 break;
2161 case HTML_O_SDNUM:
2162 pNumStr.reset(new OUString(itr->GetString()));
2163 break;
2164 case HTML_O_CLASS:
2166 // Pick up the number format associated with this class (if
2167 // any).
2168 OUString aElem("td");
2169 OUString aClass = itr->GetString();
2170 OUString aProp("mso-number-format");
2171 const ScHTMLStyles& rStyles = mpParser->GetStyles();
2172 const OUString& rVal = rStyles.getPropertyValue(aElem, aClass, aProp);
2173 if (!rVal.isEmpty())
2175 OUString aNumFmt = decodeNumberFormat(rVal);
2177 nNumberFormat = GetFormatTable()->GetEntryKey(aNumFmt);
2178 if (nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
2180 sal_Int32 nErrPos = 0;
2181 short nDummy;
2182 bool bValidFmt = GetFormatTable()->PutEntry(aNumFmt, nErrPos, nDummy, nNumberFormat);
2183 if (!bValidFmt)
2184 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
2188 break;
2192 ImplDataOn( aSpanSize );
2194 if (nNumberFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
2195 mxDataItemSet->Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat) );
2197 ProcessFormatOptions( *mxDataItemSet, rInfo );
2198 CreateNewEntry( rInfo );
2199 mxCurrEntry->pValStr = pValStr.release();
2200 mxCurrEntry->pNumStr = pNumStr.release();
2202 else
2203 CreateNewEntry( rInfo );
2206 void ScHTMLTable::DataOff( const ImportInfo& rInfo )
2208 PushEntry( rInfo, true );
2209 if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
2210 ImplDataOff();
2211 CreateNewEntry( rInfo );
2214 void ScHTMLTable::BodyOn( const ImportInfo& rInfo )
2216 bool bPushed = PushEntry( rInfo );
2217 if( !mpParentTable )
2219 // do not start new row, if nothing (no title) precedes the body.
2220 if( bPushed || !mbRowOn )
2221 ImplRowOn();
2222 if( bPushed || !mbDataOn )
2223 ImplDataOn( ScHTMLSize( 1, 1 ) );
2224 ProcessFormatOptions( *mxDataItemSet, rInfo );
2226 CreateNewEntry( rInfo );
2229 void ScHTMLTable::BodyOff( const ImportInfo& rInfo )
2231 PushEntry( rInfo );
2232 if( !mpParentTable )
2234 ImplDataOff();
2235 ImplRowOff();
2237 CreateNewEntry( rInfo );
2240 ScHTMLTable* ScHTMLTable::CloseTable( const ImportInfo& rInfo )
2242 if( mpParentTable ) // not allowed to close global table
2244 PushEntry( rInfo, mbDataOn );
2245 ImplDataOff();
2246 ImplRowOff();
2247 mpParentTable->PushTableEntry( GetTableId() );
2248 mpParentTable->CreateNewEntry( rInfo );
2249 if( mbPreFormText ) // enclose preformatted table with empty lines in parent table
2250 mpParentTable->InsertLeadingEmptyLine();
2251 return mpParentTable;
2253 return this;
2256 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
2258 const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2259 size_t nIndex = static_cast< size_t >( nCellPos );
2260 if( nIndex >= rSizes.size() ) return 0;
2261 return (nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]);
2264 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const
2266 const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2267 size_t nBeginIdx = static_cast< size_t >( ::std::max< SCCOLROW >( nCellBegin, 0 ) );
2268 size_t nEndIdx = static_cast< size_t >( ::std::min< SCCOLROW >( nCellEnd, static_cast< SCCOLROW >( rSizes.size() ) ) );
2269 if (nBeginIdx >= nEndIdx ) return 0;
2270 return rSizes[ nEndIdx - 1 ] - ((nBeginIdx == 0) ? 0 : rSizes[ nBeginIdx - 1 ]);
2273 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient ) const
2275 const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2276 return rSizes.empty() ? 0 : rSizes.back();
2279 ScHTMLSize ScHTMLTable::GetDocSize( const ScHTMLPos& rCellPos ) const
2281 ScHTMLSize aCellSpan = GetSpan( rCellPos );
2282 return ScHTMLSize(
2283 static_cast< SCCOL >( GetDocSize( tdCol, rCellPos.mnCol, rCellPos.mnCol + aCellSpan.mnCols ) ),
2284 static_cast< SCROW >( GetDocSize( tdRow, rCellPos.mnRow, rCellPos.mnRow + aCellSpan.mnRows ) ) );
2287 SCCOLROW ScHTMLTable::GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
2289 return maDocBasePos.Get( eOrient ) + GetDocSize( eOrient, 0, nCellPos );
2292 ScHTMLPos ScHTMLTable::GetDocPos( const ScHTMLPos& rCellPos ) const
2294 return ScHTMLPos(
2295 static_cast< SCCOL >( GetDocPos( tdCol, rCellPos.mnCol ) ),
2296 static_cast< SCROW >( GetDocPos( tdRow, rCellPos.mnRow ) ) );
2299 void ScHTMLTable::GetDocRange( ScRange& rRange ) const
2301 rRange.aStart = rRange.aEnd = maDocBasePos.MakeAddr();
2302 rRange.aEnd.Move( static_cast< SCsCOL >( GetDocSize( tdCol ) ) - 1, static_cast< SCsROW >( GetDocSize( tdRow ) ) - 1, 0 );
2305 void ScHTMLTable::ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const
2307 OSL_ENSURE( pDoc, "ScHTMLTable::ApplyCellBorders - no document" );
2308 if( pDoc && mbBorderOn )
2310 const SCCOL nLastCol = maSize.mnCols - 1;
2311 const SCROW nLastRow = maSize.mnRows - 1;
2312 const long nOuterLine = DEF_LINE_WIDTH_2;
2313 const long nInnerLine = DEF_LINE_WIDTH_0;
2314 SvxBorderLine aOuterLine(0, nOuterLine, table::BorderLineStyle::SOLID);
2315 SvxBorderLine aInnerLine(0, nInnerLine, table::BorderLineStyle::SOLID);
2316 SvxBoxItem aBorderItem( ATTR_BORDER );
2318 for( SCCOL nCol = 0; nCol <= nLastCol; ++nCol )
2320 SvxBorderLine* pLeftLine = (nCol == 0) ? &aOuterLine : &aInnerLine;
2321 SvxBorderLine* pRightLine = (nCol == nLastCol) ? &aOuterLine : &aInnerLine;
2322 SCCOL nCellCol1 = static_cast< SCCOL >( GetDocPos( tdCol, nCol ) ) + rFirstPos.Col();
2323 SCCOL nCellCol2 = nCellCol1 + static_cast< SCCOL >( GetDocSize( tdCol, nCol ) ) - 1;
2324 for( SCROW nRow = 0; nRow <= nLastRow; ++nRow )
2326 SvxBorderLine* pTopLine = (nRow == 0) ? &aOuterLine : &aInnerLine;
2327 SvxBorderLine* pBottomLine = (nRow == nLastRow) ? &aOuterLine : &aInnerLine;
2328 SCROW nCellRow1 = GetDocPos( tdRow, nRow ) + rFirstPos.Row();
2329 SCROW nCellRow2 = nCellRow1 + GetDocSize( tdRow, nRow ) - 1;
2330 for( SCCOL nCellCol = nCellCol1; nCellCol <= nCellCol2; ++nCellCol )
2332 aBorderItem.SetLine( (nCellCol == nCellCol1) ? pLeftLine : 0, SvxBoxItemLine::LEFT );
2333 aBorderItem.SetLine( (nCellCol == nCellCol2) ? pRightLine : 0, SvxBoxItemLine::RIGHT );
2334 for( SCROW nCellRow = nCellRow1; nCellRow <= nCellRow2; ++nCellRow )
2336 aBorderItem.SetLine( (nCellRow == nCellRow1) ? pTopLine : 0, SvxBoxItemLine::TOP );
2337 aBorderItem.SetLine( (nCellRow == nCellRow2) ? pBottomLine : 0, SvxBoxItemLine::BOTTOM );
2338 pDoc->ApplyAttr( nCellCol, nCellRow, rFirstPos.Tab(), aBorderItem );
2345 for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2346 aIter->ApplyCellBorders( pDoc, rFirstPos );
2349 SvNumberFormatter* ScHTMLTable::GetFormatTable()
2351 return mpParser->GetDoc().GetFormatTable();
2354 bool ScHTMLTable::IsEmptyCell() const
2356 return mpCurrEntryList && mpCurrEntryList->empty();
2359 bool ScHTMLTable::IsSpaceCharInfo( const ImportInfo& rInfo )
2361 return (rInfo.nToken == HTML_TEXTTOKEN) && (rInfo.aText.getLength() == 1) && (rInfo.aText[ 0 ] == ' ');
2364 ScHTMLTable::ScHTMLEntryPtr ScHTMLTable::CreateEntry() const
2366 return ScHTMLEntryPtr( new ScHTMLEntry( GetCurrItemSet() ) );
2369 void ScHTMLTable::CreateNewEntry( const ImportInfo& rInfo )
2371 OSL_ENSURE( !mxCurrEntry.get(), "ScHTMLTable::CreateNewEntry - old entry still present" );
2372 mxCurrEntry = CreateEntry();
2373 mxCurrEntry->aSel = rInfo.aSelection;
2376 void ScHTMLTable::ImplPushEntryToList( ScHTMLEntryList& rEntryList, ScHTMLEntryPtr& rxEntry )
2378 // HTML entry list does not own the entries
2379 rEntryList.push_back( rxEntry.get() );
2380 // mrEEParseList (reference to member of ScEEParser) owns the entries
2381 mrEEParseList.push_back( rxEntry.release() );
2384 bool ScHTMLTable::PushEntry( ScHTMLEntryPtr& rxEntry )
2386 bool bPushed = false;
2387 if( rxEntry.get() && rxEntry->HasContents() )
2389 if( mpCurrEntryList )
2391 if( mbPushEmptyLine )
2393 ScHTMLEntryPtr xEmptyEntry = CreateEntry();
2394 ImplPushEntryToList( *mpCurrEntryList, xEmptyEntry );
2395 mbPushEmptyLine = false;
2397 ImplPushEntryToList( *mpCurrEntryList, rxEntry );
2398 bPushed = true;
2400 else if( mpParentTable )
2402 bPushed = mpParentTable->PushEntry( rxEntry );
2404 else
2406 OSL_FAIL( "ScHTMLTable::PushEntry - cannot push entry, no parent found" );
2409 return bPushed;
2412 bool ScHTMLTable::PushEntry( const ImportInfo& rInfo, bool bLastInCell )
2414 OSL_ENSURE( mxCurrEntry.get(), "ScHTMLTable::PushEntry - no current entry" );
2415 bool bPushed = false;
2416 if( mxCurrEntry.get() )
2418 mxCurrEntry->AdjustEnd( rInfo );
2419 mxCurrEntry->Strip( mrEditEngine );
2421 // import entry always, if it is the last in cell, and cell is still empty
2422 if( bLastInCell && IsEmptyCell() )
2424 mxCurrEntry->SetImportAlways();
2425 // don't insert empty lines before single empty entries
2426 if( mxCurrEntry->IsEmpty() )
2427 mbPushEmptyLine = false;
2430 bPushed = PushEntry( mxCurrEntry );
2431 mxCurrEntry.reset();
2433 return bPushed;
2436 bool ScHTMLTable::PushTableEntry( ScHTMLTableId nTableId )
2438 OSL_ENSURE( nTableId != SC_HTML_GLOBAL_TABLE, "ScHTMLTable::PushTableEntry - cannot push global table" );
2439 bool bPushed = false;
2440 if( nTableId != SC_HTML_GLOBAL_TABLE )
2442 ScHTMLEntryPtr xEntry( new ScHTMLEntry( maTableItemSet, nTableId ) );
2443 bPushed = PushEntry( xEntry );
2445 return bPushed;
2448 ScHTMLTable* ScHTMLTable::GetExistingTable( ScHTMLTableId nTableId ) const
2450 ScHTMLTable* pTable = ((nTableId != SC_HTML_GLOBAL_TABLE) && mxNestedTables.get()) ?
2451 mxNestedTables->FindTable( nTableId, false ) : 0;
2452 OSL_ENSURE( pTable || (nTableId == SC_HTML_GLOBAL_TABLE), "ScHTMLTable::GetExistingTable - table not found" );
2453 return pTable;
2456 ScHTMLTable* ScHTMLTable::InsertNestedTable( const ImportInfo& rInfo, bool bPreFormText )
2458 if( !mxNestedTables.get() )
2459 mxNestedTables.reset( new ScHTMLTableMap( *this ) );
2460 if( bPreFormText ) // enclose new preformatted table with empty lines
2461 InsertLeadingEmptyLine();
2462 return mxNestedTables->CreateTable( rInfo, bPreFormText );
2465 void ScHTMLTable::InsertNewCell( const ScHTMLSize& rSpanSize )
2467 ScRange* pRange;
2469 /* Find an unused cell by skipping all merged ranges that cover the
2470 current cell position stored in maCurrCell. */
2471 while( ((pRange = maVMergedCells.Find( maCurrCell.MakeAddr() )) != 0) || ((pRange = maHMergedCells.Find( maCurrCell.MakeAddr() )) != 0) )
2472 maCurrCell.mnCol = pRange->aEnd.Col() + 1;
2473 mpCurrEntryList = &maEntryMap[ maCurrCell ];
2475 /* If the new cell is merged horizontally, try to find collisions with
2476 other vertically merged ranges. In this case, shrink existing
2477 vertically merged ranges (do not shrink the new cell). */
2478 SCCOL nColEnd = maCurrCell.mnCol + rSpanSize.mnCols;
2479 for( ScAddress aAddr( maCurrCell.MakeAddr() ); aAddr.Col() < nColEnd; aAddr.IncCol() )
2480 if( (pRange = maVMergedCells.Find( aAddr )) != 0 )
2481 pRange->aEnd.SetRow( maCurrCell.mnRow - 1 );
2483 // insert the new range into the cell lists
2484 ScRange aNewRange( maCurrCell.MakeAddr() );
2485 aNewRange.aEnd.Move( rSpanSize.mnCols - 1, rSpanSize.mnRows - 1, 0 );
2486 if( rSpanSize.mnRows > 1 )
2488 maVMergedCells.Append( aNewRange );
2489 /* Do not insert vertically merged ranges into maUsedCells yet,
2490 because they may be shrunken (see above). The final vertically
2491 merged ranges are inserted in FillEmptyCells(). */
2493 else
2495 if( rSpanSize.mnCols > 1 )
2496 maHMergedCells.Append( aNewRange );
2497 /* Insert horizontally merged ranges and single cells into
2498 maUsedCells, they will not be changed anymore. */
2499 maUsedCells.Join( aNewRange );
2502 // adjust table size
2503 maSize.mnCols = ::std::max< SCCOL >( maSize.mnCols, aNewRange.aEnd.Col() + 1 );
2504 maSize.mnRows = ::std::max< SCROW >( maSize.mnRows, aNewRange.aEnd.Row() + 1 );
2507 void ScHTMLTable::ImplRowOn()
2509 if( mbRowOn )
2510 ImplRowOff();
2511 mxRowItemSet.reset( new SfxItemSet( maTableItemSet ) );
2512 maCurrCell.mnCol = 0;
2513 mbRowOn = true;
2514 mbDataOn = false;
2517 void ScHTMLTable::ImplRowOff()
2519 if( mbDataOn )
2520 ImplDataOff();
2521 if( mbRowOn )
2523 mxRowItemSet.reset();
2524 ++maCurrCell.mnRow;
2525 mbRowOn = mbDataOn = false;
2529 void ScHTMLTable::ImplDataOn( const ScHTMLSize& rSpanSize )
2531 if( mbDataOn )
2532 ImplDataOff();
2533 if( !mbRowOn )
2534 ImplRowOn();
2535 mxDataItemSet.reset( new SfxItemSet( *mxRowItemSet ) );
2536 InsertNewCell( rSpanSize );
2537 mbDataOn = true;
2538 mbPushEmptyLine = false;
2541 void ScHTMLTable::ImplDataOff()
2543 if( mbDataOn )
2545 mxDataItemSet.reset();
2546 ++maCurrCell.mnCol;
2547 mpCurrEntryList = 0;
2548 mbDataOn = false;
2552 void ScHTMLTable::ProcessFormatOptions( SfxItemSet& rItemSet, const ImportInfo& rInfo )
2554 // special handling for table header cells
2555 if( rInfo.nToken == HTML_TABLEHEADER_ON )
2557 rItemSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2558 rItemSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY ) );
2561 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2562 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
2563 for (; itr != itrEnd; ++itr)
2565 switch( itr->GetToken() )
2567 case HTML_O_ALIGN:
2569 SvxCellHorJustify eVal = SVX_HOR_JUSTIFY_STANDARD;
2570 const OUString& rOptVal = itr->GetString();
2571 if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
2572 eVal = SVX_HOR_JUSTIFY_RIGHT;
2573 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
2574 eVal = SVX_HOR_JUSTIFY_CENTER;
2575 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
2576 eVal = SVX_HOR_JUSTIFY_LEFT;
2577 if( eVal != SVX_HOR_JUSTIFY_STANDARD )
2578 rItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY ) );
2580 break;
2582 case HTML_O_VALIGN:
2584 SvxCellVerJustify eVal = SVX_VER_JUSTIFY_STANDARD;
2585 const OUString& rOptVal = itr->GetString();
2586 if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
2587 eVal = SVX_VER_JUSTIFY_TOP;
2588 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
2589 eVal = SVX_VER_JUSTIFY_CENTER;
2590 else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
2591 eVal = SVX_VER_JUSTIFY_BOTTOM;
2592 if( eVal != SVX_VER_JUSTIFY_STANDARD )
2593 rItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY ) );
2595 break;
2597 case HTML_O_BGCOLOR:
2599 Color aColor;
2600 itr->GetColor( aColor );
2601 rItemSet.Put( SvxBrushItem( aColor, ATTR_BACKGROUND ) );
2603 break;
2608 void ScHTMLTable::SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize )
2610 OSL_ENSURE( nCellPos >= 0, "ScHTMLTable::SetDocSize - unexpected negative position" );
2611 ScSizeVec& rSizes = maCumSizes[ eOrient ];
2612 size_t nIndex = static_cast< size_t >( nCellPos );
2613 // expand with height/width == 1
2614 while( nIndex >= rSizes.size() )
2615 rSizes.push_back( rSizes.empty() ? 1 : (rSizes.back() + 1) );
2616 // update size of passed position and all following
2617 // #i109987# only grow, don't shrink - use the largest needed size
2618 SCsCOLROW nDiff = nSize - ((nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]));
2619 if( nDiff > 0 )
2620 for( ScSizeVec::iterator aIt = rSizes.begin() + nIndex, aEnd = rSizes.end(); aIt != aEnd; ++aIt )
2621 *aIt += nDiff;
2624 void ScHTMLTable::CalcNeededDocSize(
2625 ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nCellSpan, SCCOLROW nRealDocSize )
2627 SCCOLROW nDiffSize = 0;
2628 // in merged columns/rows: reduce needed size by size of leading columns
2629 while( nCellSpan > 1 )
2631 nDiffSize += GetDocSize( eOrient, nCellPos );
2632 --nCellSpan;
2633 ++nCellPos;
2635 // set remaining needed size to last column/row
2636 nRealDocSize -= ::std::min< SCCOLROW >( nRealDocSize - 1, nDiffSize );
2637 SetDocSize( eOrient, nCellPos, nRealDocSize );
2640 void ScHTMLTable::FillEmptyCells()
2642 for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2643 aIter->FillEmptyCells();
2645 // insert the final vertically merged ranges into maUsedCells
2646 for ( size_t i = 0, nRanges = maVMergedCells.size(); i < nRanges; ++i )
2648 ScRange* pRange = maVMergedCells[ i ];
2649 maUsedCells.Join( *pRange );
2652 for( ScAddress aAddr; aAddr.Row() < maSize.mnRows; aAddr.IncRow() )
2654 for( aAddr.SetCol( 0 ); aAddr.Col() < maSize.mnCols; aAddr.IncCol() )
2656 if( !maUsedCells.Find( aAddr ) )
2658 // create a range for the lock list (used to calc. cell span)
2659 ScRange aRange( aAddr );
2662 aRange.aEnd.IncCol();
2664 while( (aRange.aEnd.Col() < maSize.mnCols) && !maUsedCells.Find( aRange.aEnd ) );
2665 aRange.aEnd.IncCol( -1 );
2666 maUsedCells.Join( aRange );
2668 // insert a dummy entry
2669 ScHTMLEntryPtr xEntry = CreateEntry();
2670 ImplPushEntryToList( maEntryMap[ ScHTMLPos( aAddr ) ], xEntry );
2676 void ScHTMLTable::RecalcDocSize()
2678 // recalc table sizes recursively from inner to outer
2679 for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
2680 aIter->RecalcDocSize();
2682 /* Two passes: first calculates the sizes of single columns/rows, then
2683 the sizes of spanned columns/rows. This allows to fill nested tables
2684 into merged cells optimally. */
2685 static const sal_uInt16 PASS_SINGLE = 0;
2686 static const sal_uInt16 PASS_SPANNED = 1;
2687 for( sal_uInt16 nPass = PASS_SINGLE; nPass <= PASS_SPANNED; ++nPass )
2689 // iterate through every table cell
2690 ScHTMLEntryMap::const_iterator aMapIterEnd = maEntryMap.end();
2691 for( ScHTMLEntryMap::const_iterator aMapIter = maEntryMap.begin(); aMapIter != aMapIterEnd; ++aMapIter )
2693 const ScHTMLPos& rCellPos = aMapIter->first;
2694 ScHTMLSize aCellSpan = GetSpan( rCellPos );
2696 const ScHTMLEntryList& rEntryList = aMapIter->second;
2697 ScHTMLEntryList::const_iterator aListIter;
2698 ScHTMLEntryList::const_iterator aListIterEnd = rEntryList.end();
2700 // process the dimension of the current cell in this pass?
2701 // (pass is single and span is 1) or (pass is not single and span is not 1)
2702 bool bProcessColWidth = ((nPass == PASS_SINGLE) == (aCellSpan.mnCols == 1));
2703 bool bProcessRowHeight = ((nPass == PASS_SINGLE) == (aCellSpan.mnRows == 1));
2704 if( bProcessColWidth || bProcessRowHeight )
2706 ScHTMLSize aDocSize( 1, 0 ); // resulting size of the cell in document
2708 // expand the cell size for each cell parse entry
2709 for( aListIter = rEntryList.begin(); aListIter != aListIterEnd; ++aListIter )
2711 ScHTMLTable* pTable = GetExistingTable( (*aListIter)->GetTableId() );
2712 // find entry with maximum width
2713 if( bProcessColWidth && pTable )
2714 aDocSize.mnCols = ::std::max( aDocSize.mnCols, static_cast< SCCOL >( pTable->GetDocSize( tdCol ) ) );
2715 // add up height of each entry
2716 if( bProcessRowHeight )
2717 aDocSize.mnRows += pTable ? pTable->GetDocSize( tdRow ) : 1;
2719 if( !aDocSize.mnRows )
2720 aDocSize.mnRows = 1;
2722 if( bProcessColWidth )
2723 CalcNeededDocSize( tdCol, rCellPos.mnCol, aCellSpan.mnCols, aDocSize.mnCols );
2724 if( bProcessRowHeight )
2725 CalcNeededDocSize( tdRow, rCellPos.mnRow, aCellSpan.mnRows, aDocSize.mnRows );
2731 void ScHTMLTable::RecalcDocPos( const ScHTMLPos& rBasePos )
2733 maDocBasePos = rBasePos;
2734 // after the previous assignment it is allowed to call GetDocPos() methods
2736 // iterate through every table cell
2737 ScHTMLEntryMap::iterator aMapIterEnd = maEntryMap.end();
2738 for( ScHTMLEntryMap::iterator aMapIter = maEntryMap.begin(); aMapIter != aMapIterEnd; ++aMapIter )
2740 // fixed doc position of the entire cell (first entry)
2741 const ScHTMLPos aCellDocPos( GetDocPos( aMapIter->first ) );
2742 // fixed doc size of the entire cell
2743 const ScHTMLSize aCellDocSize( GetDocSize( aMapIter->first ) );
2745 // running doc position for single entries
2746 ScHTMLPos aEntryDocPos( aCellDocPos );
2748 ScHTMLEntryList& rEntryList = aMapIter->second;
2749 ScHTMLEntry* pEntry = 0;
2750 ScHTMLEntryList::iterator aListIterEnd = rEntryList.end();
2751 for( ScHTMLEntryList::iterator aListIter = rEntryList.begin(); aListIter != aListIterEnd; ++aListIter )
2753 pEntry = *aListIter;
2754 if( ScHTMLTable* pTable = GetExistingTable( pEntry->GetTableId() ) )
2756 pTable->RecalcDocPos( aEntryDocPos ); // recalc nested table
2757 pEntry->nCol = SCCOL_MAX;
2758 pEntry->nRow = SCROW_MAX;
2759 SCROW nTableRows = static_cast< SCROW >( pTable->GetDocSize( tdRow ) );
2761 // use this entry to pad empty space right of table
2762 if( mpParentTable ) // ... but not in global table
2764 SCCOL nStartCol = aEntryDocPos.mnCol + static_cast< SCCOL >( pTable->GetDocSize( tdCol ) );
2765 SCCOL nNextCol = aEntryDocPos.mnCol + aCellDocSize.mnCols;
2766 if( nStartCol < nNextCol )
2768 pEntry->nCol = nStartCol;
2769 pEntry->nRow = aEntryDocPos.mnRow;
2770 pEntry->nColOverlap = nNextCol - nStartCol;
2771 pEntry->nRowOverlap = nTableRows;
2774 aEntryDocPos.mnRow += nTableRows;
2776 else
2778 pEntry->nCol = aEntryDocPos.mnCol;
2779 pEntry->nRow = aEntryDocPos.mnRow;
2780 if( mpParentTable ) // do not merge in global table
2781 pEntry->nColOverlap = aCellDocSize.mnCols;
2782 ++aEntryDocPos.mnRow;
2786 // pEntry points now to last entry.
2787 if( pEntry )
2789 if( (pEntry == rEntryList.front()) && (pEntry->GetTableId() == SC_HTML_NO_TABLE) )
2791 // pEntry is the only entry in this cell - merge rows of cell with single non-table entry.
2792 pEntry->nRowOverlap = aCellDocSize.mnRows;
2794 else
2796 // fill up incomplete entry lists
2797 SCROW nFirstUnusedRow = aCellDocPos.mnRow + aCellDocSize.mnRows;
2798 while( aEntryDocPos.mnRow < nFirstUnusedRow )
2800 ScHTMLEntryPtr xDummyEntry( new ScHTMLEntry( pEntry->GetItemSet() ) );
2801 xDummyEntry->nCol = aEntryDocPos.mnCol;
2802 xDummyEntry->nRow = aEntryDocPos.mnRow;
2803 xDummyEntry->nColOverlap = aCellDocSize.mnCols;
2804 ImplPushEntryToList( rEntryList, xDummyEntry );
2805 ++aEntryDocPos.mnRow;
2812 ScHTMLGlobalTable::ScHTMLGlobalTable(
2813 SfxItemPool& rPool,
2814 EditEngine& rEditEngine,
2815 ::std::vector< ScEEParseEntry* >& rEEParseList,
2816 ScHTMLTableId& rnUnusedId,
2817 ScHTMLParser* pParser
2819 ScHTMLTable( rPool, rEditEngine, rEEParseList, rnUnusedId, pParser )
2823 ScHTMLGlobalTable::~ScHTMLGlobalTable()
2827 void ScHTMLGlobalTable::Recalc()
2829 // Fills up empty cells with a dummy entry. */
2830 FillEmptyCells();
2831 // recalc table sizes of all nested tables and this table
2832 RecalcDocSize();
2833 // recalc document positions of all entries in this table and in nested tables
2834 RecalcDocPos( GetDocPos() );
2837 ScHTMLQueryParser::ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
2838 ScHTMLParser( pEditEngine, pDoc ),
2839 mnUnusedId( SC_HTML_GLOBAL_TABLE ),
2840 mbTitleOn( false )
2842 mxGlobTable.reset(
2843 new ScHTMLGlobalTable(*pPool, *pEdit, maList, mnUnusedId, this));
2844 mpCurrTable = mxGlobTable.get();
2847 ScHTMLQueryParser::~ScHTMLQueryParser()
2851 sal_uLong ScHTMLQueryParser::Read( SvStream& rStrm, const OUString& rBaseURL )
2853 SvKeyValueIteratorRef xValues;
2854 SvKeyValueIterator* pAttributes = 0;
2856 SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
2857 if( pObjSh && pObjSh->IsLoading() )
2859 pAttributes = pObjSh->GetHeaderAttributes();
2861 else
2863 /* When not loading, set up fake HTTP headers to force the SfxHTMLParser
2864 to use UTF8 (used when pasting from clipboard) */
2865 const sal_Char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
2866 if( pCharSet )
2868 OUString aContentType = "text/html; charset=";
2869 aContentType += OUString::createFromAscii( pCharSet );
2871 xValues = new SvKeyValueIterator;
2872 xValues->Append( SvKeyValue( OUString( OOO_STRING_SVTOOLS_HTML_META_content_type ), aContentType ) );
2873 pAttributes = xValues;
2877 Link<> aOldLink = pEdit->GetImportHdl();
2878 pEdit->SetImportHdl( LINK( this, ScHTMLQueryParser, HTMLImportHdl ) );
2879 sal_uLong nErr = pEdit->Read( rStrm, rBaseURL, EE_FORMAT_HTML, pAttributes );
2880 pEdit->SetImportHdl( aOldLink );
2882 mxGlobTable->Recalc();
2883 nColMax = static_cast< SCCOL >( mxGlobTable->GetDocSize( tdCol ) - 1 );
2884 nRowMax = static_cast< SCROW >( mxGlobTable->GetDocSize( tdRow ) - 1 );
2886 return nErr;
2889 const ScHTMLTable* ScHTMLQueryParser::GetGlobalTable() const
2891 return mxGlobTable.get();
2894 void ScHTMLQueryParser::ProcessToken( const ImportInfo& rInfo )
2896 switch( rInfo.nToken )
2898 // --- meta data ---
2899 case HTML_META: MetaOn( rInfo ); break; // <meta>
2901 // --- title handling ---
2902 case HTML_TITLE_ON: TitleOn( rInfo ); break; // <title>
2903 case HTML_TITLE_OFF: TitleOff( rInfo ); break; // </title>
2905 case HTML_STYLE_ON: break;
2906 case HTML_STYLE_OFF: ParseStyle(rInfo.aText); break;
2908 // --- body handling ---
2909 case HTML_BODY_ON: mpCurrTable->BodyOn( rInfo ); break; // <body>
2910 case HTML_BODY_OFF: mpCurrTable->BodyOff( rInfo ); break; // </body>
2912 // --- insert text ---
2913 case HTML_TEXTTOKEN: InsertText( rInfo ); break; // any text
2914 case HTML_LINEBREAK: mpCurrTable->BreakOn(); break; // <br>
2915 case HTML_HEAD1_ON: // <h1>
2916 case HTML_HEAD2_ON: // <h2>
2917 case HTML_HEAD3_ON: // <h3>
2918 case HTML_HEAD4_ON: // <h4>
2919 case HTML_HEAD5_ON: // <h5>
2920 case HTML_HEAD6_ON: // <h6>
2921 case HTML_PARABREAK_ON: mpCurrTable->HeadingOn(); break; // <p>
2923 // --- misc. contents ---
2924 case HTML_ANCHOR_ON: mpCurrTable->AnchorOn(); break; // <a>
2926 // --- table handling ---
2927 case HTML_TABLE_ON: TableOn( rInfo ); break; // <table>
2928 case HTML_TABLE_OFF: TableOff( rInfo ); break; // </table>
2929 case HTML_TABLEROW_ON: mpCurrTable->RowOn( rInfo ); break; // <tr>
2930 case HTML_TABLEROW_OFF: mpCurrTable->RowOff( rInfo ); break; // </tr>
2931 case HTML_TABLEHEADER_ON: // <th>
2932 case HTML_TABLEDATA_ON: mpCurrTable->DataOn( rInfo ); break; // <td>
2933 case HTML_TABLEHEADER_OFF: // </th>
2934 case HTML_TABLEDATA_OFF: mpCurrTable->DataOff( rInfo ); break; // </td>
2935 case HTML_PREFORMTXT_ON: PreOn( rInfo ); break; // <pre>
2936 case HTML_PREFORMTXT_OFF: PreOff( rInfo ); break; // </pre>
2938 // --- formatting ---
2939 case HTML_FONT_ON: FontOn( rInfo ); break; // <font>
2941 case HTML_BIGPRINT_ON: // <big>
2942 //! TODO: store current font size, use following size
2943 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 3 ], 100, ATTR_FONT_HEIGHT ) );
2944 break;
2945 case HTML_SMALLPRINT_ON: // <small>
2946 //! TODO: store current font size, use preceding size
2947 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 0 ], 100, ATTR_FONT_HEIGHT ) );
2948 break;
2950 case HTML_BOLD_ON: // <b>
2951 case HTML_STRONG_ON: // <strong>
2952 mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2953 break;
2955 case HTML_ITALIC_ON: // <i>
2956 case HTML_EMPHASIS_ON: // <em>
2957 case HTML_ADDRESS_ON: // <address>
2958 case HTML_BLOCKQUOTE_ON: // <blockquote>
2959 case HTML_BLOCKQUOTE30_ON: // <bq>
2960 case HTML_CITIATION_ON: // <cite>
2961 case HTML_VARIABLE_ON: // <var>
2962 mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
2963 break;
2965 case HTML_DEFINSTANCE_ON: // <dfn>
2966 mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2967 mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
2968 break;
2970 case HTML_UNDERLINE_ON: // <u>
2971 mpCurrTable->PutItem( SvxUnderlineItem( UNDERLINE_SINGLE, ATTR_FONT_UNDERLINE ) );
2972 break;
2976 void ScHTMLQueryParser::InsertText( const ImportInfo& rInfo )
2978 mpCurrTable->PutText( rInfo );
2979 if( mbTitleOn )
2980 maTitle.append(rInfo.aText);
2983 void ScHTMLQueryParser::FontOn( const ImportInfo& rInfo )
2985 const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
2986 HTMLOptions::const_iterator itr = rOptions.begin(), itrEnd = rOptions.end();
2987 for (; itr != itrEnd; ++itr)
2989 switch( itr->GetToken() )
2991 case HTML_O_FACE :
2993 const OUString& rFace = itr->GetString();
2994 OUString aFontName;
2995 sal_Int32 nPos = 0;
2996 while( nPos != -1 )
2998 // font list separator: VCL = ';' HTML = ','
2999 OUString aFName = comphelper::string::strip(rFace.getToken(0, ',', nPos), ' ');
3000 aFontName = ScGlobal::addToken(aFontName, aFName, ';');
3002 if ( !aFontName.isEmpty() )
3003 mpCurrTable->PutItem( SvxFontItem( FAMILY_DONTKNOW,
3004 aFontName, EMPTY_OUSTRING, PITCH_DONTKNOW,
3005 RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
3007 break;
3008 case HTML_O_SIZE :
3010 sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( itr->GetNumber(), 1, SC_HTML_FONTSIZES );
3011 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ nSize - 1 ], 100, ATTR_FONT_HEIGHT ) );
3013 break;
3014 case HTML_O_COLOR :
3016 Color aColor;
3017 itr->GetColor( aColor );
3018 mpCurrTable->PutItem( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
3020 break;
3025 void ScHTMLQueryParser::MetaOn( const ImportInfo& rInfo )
3027 if( mpDoc->GetDocumentShell() )
3029 HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
3031 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3032 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
3033 pParser->ParseMetaOptions(
3034 xDPS->getDocumentProperties(),
3035 mpDoc->GetDocumentShell()->GetHeaderAttributes() );
3039 void ScHTMLQueryParser::TitleOn( const ImportInfo& /*rInfo*/ )
3041 mbTitleOn = true;
3042 maTitle.makeStringAndClear();
3045 void ScHTMLQueryParser::TitleOff( const ImportInfo& rInfo )
3047 if( mbTitleOn )
3049 OUString aTitle = maTitle.makeStringAndClear().trim();
3050 if (!aTitle.isEmpty() && mpDoc->GetDocumentShell())
3052 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
3053 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
3055 xDPS->getDocumentProperties()->setTitle(aTitle);
3057 InsertText( rInfo );
3058 mbTitleOn = false;
3062 void ScHTMLQueryParser::TableOn( const ImportInfo& rInfo )
3064 mpCurrTable = mpCurrTable->TableOn( rInfo );
3067 void ScHTMLQueryParser::TableOff( const ImportInfo& rInfo )
3069 mpCurrTable = mpCurrTable->TableOff( rInfo );
3072 void ScHTMLQueryParser::PreOn( const ImportInfo& rInfo )
3074 mpCurrTable = mpCurrTable->PreOn( rInfo );
3077 void ScHTMLQueryParser::PreOff( const ImportInfo& rInfo )
3079 mpCurrTable = mpCurrTable->PreOff( rInfo );
3082 void ScHTMLQueryParser::CloseTable( const ImportInfo& rInfo )
3084 mpCurrTable = mpCurrTable->CloseTable( rInfo );
3087 #if ENABLE_ORCUS
3089 namespace {
3092 * Handler class for the CSS parser.
3094 class CSSHandler
3096 struct MemStr
3098 const char* mp;
3099 size_t mn;
3101 MemStr() : mp(NULL), mn(0) {}
3102 MemStr(const char* p, size_t n) : mp(p), mn(n) {}
3103 MemStr(const MemStr& r) : mp(r.mp), mn(r.mn) {}
3104 MemStr& operator=(const MemStr& r)
3106 mp = r.mp;
3107 mn = r.mn;
3108 return *this;
3112 typedef std::pair<MemStr, MemStr> SelectorName; // element : class
3113 typedef std::vector<SelectorName> SelectorNames;
3114 SelectorNames maSelectorNames; /// current selector names.
3115 MemStr maPropName; /// current property name.
3116 MemStr maPropValue; /// current property value.
3118 ScHTMLStyles& mrStyles;
3119 public:
3120 CSSHandler(ScHTMLStyles& rStyles) : mrStyles(rStyles) {}
3122 static void at_rule_name(const char* /*p*/, size_t /*n*/)
3124 // TODO: For now, we ignore at-rule properties.
3127 void selector_name(const char* p_elem, size_t n_elem, const char* p_class, size_t n_class)
3129 MemStr aElem(p_elem, n_elem), aClass(p_class, n_class);
3130 SelectorName aName(aElem, aClass);
3131 maSelectorNames.push_back(aName);
3134 void property_name(const char* p, size_t n)
3136 maPropName = MemStr(p, n);
3139 void value(const char* p, size_t n)
3141 maPropValue = MemStr(p, n);
3144 static void begin_parse() {}
3146 static void end_parse() {}
3148 static void begin_block() {}
3150 void end_block()
3152 maSelectorNames.clear();
3155 static void begin_property() {}
3157 void end_property()
3159 SelectorNames::const_iterator itr = maSelectorNames.begin(), itrEnd = maSelectorNames.end();
3160 for (; itr != itrEnd; ++itr)
3162 // Add this property to the collection for each selector.
3163 const SelectorName& rSelName = *itr;
3164 const MemStr& rElem = rSelName.first;
3165 const MemStr& rClass = rSelName.second;
3166 OUString aName(maPropName.mp, maPropName.mn, RTL_TEXTENCODING_UTF8);
3167 OUString aValue(maPropValue.mp, maPropValue.mn, RTL_TEXTENCODING_UTF8);
3168 mrStyles.add(rElem.mp, rElem.mn, rClass.mp, rClass.mn, aName, aValue);
3170 maPropName = MemStr();
3171 maPropValue = MemStr();
3177 void ScHTMLQueryParser::ParseStyle(const OUString& rStrm)
3179 OString aStr = OUStringToOString(rStrm, RTL_TEXTENCODING_UTF8);
3180 CSSHandler aHdl(GetStyles());
3181 orcus::css_parser<CSSHandler> aParser(aStr.getStr(), aStr.getLength(), aHdl);
3184 aParser.parse();
3186 catch (const orcus::css_parse_error&)
3188 // TODO: Parsing of CSS failed. Do nothing for now.
3192 #else
3194 void ScHTMLQueryParser::ParseStyle(const OUString&) {}
3196 #endif
3198 IMPL_LINK( ScHTMLQueryParser, HTMLImportHdl, const ImportInfo*, pInfo )
3200 switch( pInfo->eState )
3202 case HTMLIMP_START:
3203 break;
3205 case HTMLIMP_NEXTTOKEN:
3206 case HTMLIMP_UNKNOWNATTR:
3207 ProcessToken( *pInfo );
3208 break;
3210 case HTMLIMP_INSERTPARA:
3211 mpCurrTable->InsertPara( *pInfo );
3212 break;
3214 case HTMLIMP_SETATTR:
3215 case HTMLIMP_INSERTTEXT:
3216 case HTMLIMP_INSERTFIELD:
3217 break;
3219 case HTMLIMP_END:
3220 while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
3221 CloseTable( *pInfo );
3222 break;
3224 default:
3225 OSL_FAIL( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
3227 return 0;
3230 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */