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