xref: /trunk/main/sc/source/filter/html/htmlpars.cxx (revision b77af630)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_scfilt.hxx"
26 
27 #include <boost/shared_ptr.hpp>
28 
29 #define SC_HTMLPARS_CXX
30 #include "scitems.hxx"
31 #include <editeng/eeitem.hxx>
32 
33 #include <svtools/htmlcfg.hxx>
34 #include <svx/algitem.hxx>
35 #include <editeng/colritem.hxx>
36 #include <editeng/brshitem.hxx>
37 #include <editeng/editeng.hxx>
38 #include <editeng/fhgtitem.hxx>
39 #include <editeng/fontitem.hxx>
40 #include <editeng/postitem.hxx>
41 #include <editeng/udlnitem.hxx>
42 #include <editeng/wghtitem.hxx>
43 #include <editeng/boxitem.hxx>
44 #include <sfx2/objsh.hxx>
45 #include <svl/eitem.hxx>
46 #include <svtools/filter.hxx>
47 #include <svtools/parhtml.hxx>
48 #include <svtools/htmlkywd.hxx>
49 #include <svtools/htmltokn.h>
50 #include <sfx2/docfile.hxx>
51 
52 #include <vcl/svapp.hxx>
53 #include <tools/urlobj.hxx>
54 #include <tools/tenccvt.hxx>
55 
56 #include "htmlpars.hxx"
57 #include "global.hxx"
58 #include "document.hxx"
59 #include "rangelst.hxx"
60 
61 #include <com/sun/star/document/XDocumentProperties.hpp>
62 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
63 
64 
65 using namespace ::com::sun::star;
66 
67 
68 SV_IMPL_VARARR_SORT( ScHTMLColOffset, sal_uLong );
69 
70 
71 // ============================================================================
72 // BASE class for HTML parser classes
73 // ============================================================================
74 
ScHTMLParser(EditEngine * pEditEngine,ScDocument * pDoc)75 ScHTMLParser::ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
76     ScEEParser( pEditEngine ),
77     mpDoc( pDoc )
78 {
79 	SvxHtmlOptions* pHtmlOptions = SvxHtmlOptions::Get();
80     for( sal_uInt16 nIndex = 0; nIndex < SC_HTML_FONTSIZES; ++nIndex )
81         maFontHeights[ nIndex ] = pHtmlOptions->GetFontSize( nIndex ) * 20;
82 }
83 
~ScHTMLParser()84 ScHTMLParser::~ScHTMLParser()
85 {
86 }
87 
88 
89 // ============================================================================
90 
ScHTMLLayoutParser(EditEngine * pEditP,const String & rBaseURL,const Size & aPageSizeP,ScDocument * pDocP)91 ScHTMLLayoutParser::ScHTMLLayoutParser( EditEngine* pEditP, const String& rBaseURL, const Size& aPageSizeP, ScDocument* pDocP ) :
92         ScHTMLParser( pEditP, pDocP ),
93         aPageSize( aPageSizeP ),
94         aBaseURL( rBaseURL ),
95         xLockedList( new ScRangeList ),
96         pTables( NULL ),
97         pColOffset( new ScHTMLColOffset ),
98         pLocalColOffset( new ScHTMLColOffset ),
99         nFirstTableCell(0),
100         nTableLevel(0),
101         nTable(0),
102         nMaxTable(0),
103         nColCntStart(0),
104         nMaxCol(0),
105         nTableWidth(0),
106         nColOffset(0),
107         nColOffsetStart(0),
108         nMetaCnt(0),
109         nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
110         bTabInTabCell( sal_False ),
111         bFirstRow( sal_True ),
112         bInCell( sal_False ),
113         bInTitle( sal_False )
114 {
115     MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
116     MakeColNoRef( pColOffset, 0, 0, 0, 0 );
117 }
118 
119 
~ScHTMLLayoutParser()120 ScHTMLLayoutParser::~ScHTMLLayoutParser()
121 {
122     ScHTMLTableStackEntry* pS;
123     while ( (pS = aTableStack.Pop()) != 0 )
124     {
125         if ( pList->GetPos( pS->pCellEntry ) == LIST_ENTRY_NOTFOUND )
126             delete pS->pCellEntry;
127         if ( pS->pLocalColOffset != pLocalColOffset )
128             delete pS->pLocalColOffset;
129         delete pS;
130     }
131     if ( pLocalColOffset )
132         delete pLocalColOffset;
133     if ( pColOffset )
134         delete pColOffset;
135     if ( pTables )
136     {
137         for ( Table* pT = (Table*) pTables->First(); pT; pT = (Table*) pTables->Next() )
138             delete pT;
139         delete pTables;
140     }
141 }
142 
143 
Read(SvStream & rStream,const String & rBaseURL)144 sal_uLong ScHTMLLayoutParser::Read( SvStream& rStream, const String& rBaseURL )
145 {
146     Link aOldLink = pEdit->GetImportHdl();
147     pEdit->SetImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
148 
149     SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
150     sal_Bool bLoading = pObjSh && pObjSh->IsLoading();
151 
152     SvKeyValueIteratorRef xValues;
153     SvKeyValueIterator* pAttributes = NULL;
154     if ( bLoading )
155         pAttributes = pObjSh->GetHeaderAttributes();
156     else
157     {
158         //  When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
159         //  (used when pasting from clipboard)
160 
161         const sal_Char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
162         if( pCharSet )
163         {
164             String aContentType = String::CreateFromAscii( "text/html; charset=" );
165             aContentType.AppendAscii( pCharSet );
166 
167             xValues = new SvKeyValueIterator;
168             xValues->Append( SvKeyValue( String::CreateFromAscii( OOO_STRING_SVTOOLS_HTML_META_content_type ), aContentType ) );
169             pAttributes = xValues;
170         }
171     }
172 
173     sal_uLong nErr = pEdit->Read( rStream, rBaseURL, EE_FORMAT_HTML, pAttributes );
174 
175     pEdit->SetImportHdl( aOldLink );
176     // Spaltenbreiten erzeugen
177     Adjust();
178     OutputDevice* pDefaultDev = Application::GetDefaultDevice();
179     sal_uInt16 nCount = pColOffset->Count();
180     const sal_uLong* pOff = (const sal_uLong*) pColOffset->GetData();
181     sal_uLong nOff = *pOff++;
182     Size aSize;
183     for ( sal_uInt16 j = 1; j < nCount; j++, pOff++ )
184     {
185         aSize.Width() = *pOff - nOff;
186         aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MAP_TWIP ) );
187         pColWidths->Insert( j-1, (void*)aSize.Width() );
188         nOff = *pOff;
189     }
190     return nErr;
191 }
192 
193 
GetGlobalTable() const194 const ScHTMLTable* ScHTMLLayoutParser::GetGlobalTable() const
195 {
196     return 0;
197 }
198 
199 
NewActEntry(ScEEParseEntry * pE)200 void ScHTMLLayoutParser::NewActEntry( ScEEParseEntry* pE )
201 {
202     ScEEParser::NewActEntry( pE );
203     if ( pE )
204     {
205         if ( !pE->aSel.HasRange() )
206         {   // komplett leer, nachfolgender Text landet im gleichen Absatz!
207             pActEntry->aSel.nStartPara = pE->aSel.nEndPara;
208             pActEntry->aSel.nStartPos = pE->aSel.nEndPos;
209         }
210     }
211     pActEntry->aSel.nEndPara = pActEntry->aSel.nStartPara;
212     pActEntry->aSel.nEndPos = pActEntry->aSel.nStartPos;
213 }
214 
215 
EntryEnd(ScEEParseEntry * pE,const ESelection & rSel)216 void ScHTMLLayoutParser::EntryEnd( ScEEParseEntry* pE, const ESelection& rSel )
217 {
218     if ( rSel.nEndPara >= pE->aSel.nStartPara )
219     {
220         pE->aSel.nEndPara = rSel.nEndPara;
221         pE->aSel.nEndPos = rSel.nEndPos;
222     }
223     else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
224     {   // kein Absatz angehaengt aber leer, nichts tun
225     }
226     else
227     {
228         DBG_ERRORFILE( "EntryEnd: EditEngine ESelection End < Start" );
229     }
230 }
231 
232 
NextRow(ImportInfo * pInfo)233 void ScHTMLLayoutParser::NextRow( ImportInfo* pInfo )
234 {
235     if ( bInCell )
236         CloseEntry( pInfo );
237     if ( nRowMax < ++nRowCnt )
238         nRowMax = nRowCnt;
239     nColCnt = nColCntStart;
240     nColOffset = nColOffsetStart;
241     bFirstRow = sal_False;
242 }
243 
244 
SeekOffset(ScHTMLColOffset * pOffset,sal_uInt16 nOffset,SCCOL * pCol,sal_uInt16 nOffsetTol)245 sal_Bool ScHTMLLayoutParser::SeekOffset( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
246         SCCOL* pCol, sal_uInt16 nOffsetTol )
247 {
248     DBG_ASSERT( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
249     sal_uInt16 nPos;
250     sal_Bool bFound = pOffset->Seek_Entry( nOffset, &nPos );
251     *pCol = static_cast<SCCOL>(nPos);
252     if ( bFound )
253         return sal_True;
254     sal_uInt16 nCount = pOffset->Count();
255     if ( !nCount )
256         return sal_False;
257     // nPos ist Einfuegeposition, da liegt der Naechsthoehere (oder auch nicht)
258     if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
259         return sal_True;
260     // nicht kleiner als alles andere? dann mit Naechstniedrigerem vergleichen
261     else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
262     {
263         (*pCol)--;
264         return sal_True;
265     }
266     return sal_False;
267 }
268 
269 
MakeCol(ScHTMLColOffset * pOffset,sal_uInt16 & nOffset,sal_uInt16 & nWidth,sal_uInt16 nOffsetTol,sal_uInt16 nWidthTol)270 void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
271         sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
272 {
273     DBG_ASSERT( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
274     SCCOL nPos;
275     if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
276         nOffset = (sal_uInt16)(*pOffset)[nPos];
277     else
278         pOffset->Insert( nOffset );
279     if ( nWidth )
280     {
281         if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
282             nWidth = (sal_uInt16)(*pOffset)[nPos] - nOffset;
283         else
284             pOffset->Insert( nOffset + nWidth );
285     }
286 }
287 
288 
MakeColNoRef(ScHTMLColOffset * pOffset,sal_uInt16 nOffset,sal_uInt16 nWidth,sal_uInt16 nOffsetTol,sal_uInt16 nWidthTol)289 void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
290         sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
291 {
292     DBG_ASSERT( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
293     SCCOL nPos;
294     if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
295         nOffset = (sal_uInt16)(*pOffset)[nPos];
296     else
297         pOffset->Insert( nOffset );
298     if ( nWidth )
299     {
300         if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
301             pOffset->Insert( nOffset + nWidth );
302     }
303 }
304 
305 
ModifyOffset(ScHTMLColOffset * pOffset,sal_uInt16 & nOldOffset,sal_uInt16 & nNewOffset,sal_uInt16 nOffsetTol)306 void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
307             sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
308 {
309     DBG_ASSERT( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
310     SCCOL nPos;
311     if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
312     {
313         if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
314             nNewOffset = (sal_uInt16)(*pOffset)[nPos];
315         else
316             pOffset->Insert( nNewOffset );
317         return ;
318     }
319     nOldOffset = (sal_uInt16)(*pOffset)[nPos];
320     SCCOL nPos2;
321     if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
322     {
323         nNewOffset = (sal_uInt16)(*pOffset)[nPos2];
324         return ;
325     }
326     sal_uLong* pData = ((sal_uLong*) pOffset->GetData()) + nPos;        //! QAD
327     long nDiff = nNewOffset - nOldOffset;
328     if ( nDiff < 0 )
329     {
330         const sal_uLong* pStop = pOffset->GetData();
331         do
332         {
333             *pData += nDiff;
334         } while ( pStop < pData-- );
335     }
336     else
337     {
338         const sal_uLong* pStop = pOffset->GetData() + pOffset->Count();
339         do
340         {
341             *pData += nDiff;
342         } while ( ++pData < pStop );
343     }
344 }
345 
346 
SkipLocked(ScEEParseEntry * pE,sal_Bool bJoin)347 void ScHTMLLayoutParser::SkipLocked( ScEEParseEntry* pE, sal_Bool bJoin )
348 {
349     if ( ValidCol(pE->nCol) )
350     {   // wuerde sonst bei ScAddress falschen Wert erzeugen, evtl. Endlosschleife!
351         sal_Bool bBadCol = sal_False;
352         sal_Bool bAgain;
353         ScRange aRange( pE->nCol, pE->nRow, 0,
354             pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
355         do
356         {
357             bAgain = sal_False;
358             for ( ScRange* pR = xLockedList->First(); pR; pR = xLockedList->Next() )
359             {
360                 if ( pR->Intersects( aRange ) )
361                 {
362                     pE->nCol = pR->aEnd.Col() + 1;
363                     SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
364                     if ( pE->nCol > MAXCOL || nTmp > MAXCOL )
365                         bBadCol = sal_True;
366                     else
367                     {
368                         bAgain = sal_True;
369                         aRange.aStart.SetCol( pE->nCol );
370                         aRange.aEnd.SetCol( nTmp );
371                     }
372                     break;
373                 }
374             }
375         } while ( bAgain );
376         if ( bJoin && !bBadCol )
377             xLockedList->Join( aRange );
378     }
379 }
380 
381 
Adjust()382 void ScHTMLLayoutParser::Adjust()
383 {
384     for ( ScRange* pR = xLockedList->First(); pR; pR = xLockedList->Next() )
385         delete pR;
386     xLockedList->Clear();
387     ScHTMLAdjustStack aStack;
388     ScHTMLAdjustStackEntry* pS;
389     sal_uInt16 nTab = 0;
390     SCCOL nLastCol = SCCOL_MAX;
391     SCROW nNextRow = 0;
392     SCROW nCurRow = 0;
393     sal_uInt16 nPageWidth = (sal_uInt16) aPageSize.Width();
394     Table* pTab = NULL;
395     for ( ScEEParseEntry* pE = pList->First(); pE; pE = pList->Next() )
396     {
397         if ( pE->nTab < nTab )
398         {   // Table beendet
399             if ( (pS = aStack.Pop()) != 0 )
400             {
401                 nLastCol = pS->nLastCol;
402                 nNextRow = pS->nNextRow;
403                 nCurRow = pS->nCurRow;
404             }
405             delete pS;
406             nTab = pE->nTab;
407             pTab = (pTables ? (Table*) pTables->Get( nTab ) : NULL);
408 
409         }
410         SCROW nRow = pE->nRow;
411         if ( pE->nCol <= nLastCol )
412         {   // naechste Zeile
413             if ( pE->nRow < nNextRow )
414                 pE->nRow = nCurRow = nNextRow;
415             else
416                 nCurRow = nNextRow = pE->nRow;
417             SCROW nR;
418             if ( pTab && ((nR = (SCROW)(sal_uLong)pTab->Get( nCurRow )) != 0) )
419                 nNextRow += nR;
420             else
421                 nNextRow++;
422         }
423         else
424             pE->nRow = nCurRow;
425         nLastCol = pE->nCol;    // eingelesene Col
426         if ( pE->nTab > nTab )
427         {   // neue Table
428             aStack.Push( new ScHTMLAdjustStackEntry(
429                 nLastCol, nNextRow, nCurRow ) );
430             nTab = pE->nTab;
431             pTab = (pTables ? (Table*) pTables->Get( nTab ) : NULL);
432             // neuer Zeilenabstand
433             SCROW nR;
434             if ( pTab && ((nR = (SCROW)(sal_uLong)pTab->Get( nCurRow )) != 0) )
435                 nNextRow = nCurRow + nR;
436             else
437                 nNextRow = nCurRow + 1;
438         }
439         if ( nTab == 0 )
440             pE->nWidth = nPageWidth;
441         else
442         {   // echte Table, keine Absaetze auf der Wiese
443             if ( pTab )
444             {
445                 SCROW nRowSpan = pE->nRowOverlap;
446                 for ( SCROW j=0; j < nRowSpan; j++ )
447                 {   // aus merged Zeilen resultierendes RowSpan
448                     SCROW nRows = (SCROW)(sal_uLong)pTab->Get( nRow+j );
449                     if ( nRows > 1 )
450                     {
451                         pE->nRowOverlap += nRows - 1;
452                         if ( j == 0 )
453                         {   // merged Zeilen verschieben die naechste Zeile
454                             SCROW nTmp = nCurRow + nRows;
455                             if ( nNextRow < nTmp )
456                                 nNextRow = nTmp;
457                         }
458                     }
459                 }
460             }
461         }
462         // echte Col
463         SeekOffset( pColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
464         SCCOL nColBeforeSkip = pE->nCol;
465         SkipLocked( pE, sal_False );
466         if ( pE->nCol != nColBeforeSkip )
467         {
468             SCCOL nCount = (SCCOL)pColOffset->Count();
469             if ( nCount <= pE->nCol )
470             {
471                 pE->nOffset = (sal_uInt16) (*pColOffset)[nCount-1];
472                 MakeCol( pColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
473             }
474             else
475             {
476                 pE->nOffset = (sal_uInt16) (*pColOffset)[pE->nCol];
477             }
478         }
479         SCCOL nPos;
480         if ( pE->nWidth && SeekOffset( pColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
481             pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
482         else
483         {
484 //2do: das muss nicht korrekt sein, ist aber..
485             pE->nColOverlap = 1;
486         }
487         xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
488             pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
489         // MaxDimensions mitfuehren
490         SCCOL nColTmp = pE->nCol + pE->nColOverlap;
491         if ( nColMax < nColTmp )
492             nColMax = nColTmp;
493         SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
494         if ( nRowMax < nRowTmp )
495             nRowMax = nRowTmp;
496     }
497     while ( (pS = aStack.Pop()) != 0 )
498         delete pS;
499 }
500 
501 
GetWidth(ScEEParseEntry * pE)502 sal_uInt16 ScHTMLLayoutParser::GetWidth( ScEEParseEntry* pE )
503 {
504     if ( pE->nWidth )
505         return pE->nWidth;
506     sal_Int32 nTmp = ::std::min( static_cast<sal_Int32>( pE->nCol -
507                 nColCntStart + pE->nColOverlap),
508             static_cast<sal_Int32>( pLocalColOffset->Count() - 1));
509     SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
510     sal_uInt16 nOff2 = (sal_uInt16) (*pLocalColOffset)[nPos];
511     if ( pE->nOffset < nOff2 )
512         return nOff2 - pE->nOffset;
513     return 0;
514 }
515 
516 
SetWidths()517 void ScHTMLLayoutParser::SetWidths()
518 {
519     ScEEParseEntry* pE;
520     SCCOL nCol;
521     if ( !nTableWidth )
522         nTableWidth = (sal_uInt16) aPageSize.Width();
523     SCCOL nColsPerRow = nMaxCol - nColCntStart;
524     if ( nColsPerRow <= 0 )
525         nColsPerRow = 1;
526     if ( pLocalColOffset->Count() <= 2 )
527     {   // nur PageSize, es gab keine Width-Angabe
528         sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
529         sal_uInt16 nOff = nColOffsetStart;
530         pLocalColOffset->Remove( (sal_uInt16)0, pLocalColOffset->Count() );
531         for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
532         {
533             MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
534         }
535         nTableWidth = (sal_uInt16)((*pLocalColOffset)[pLocalColOffset->Count() -1 ] - (*pLocalColOffset)[0]);
536         pE = pList->Seek( nFirstTableCell );
537         while ( pE )
538         {
539             if ( pE->nTab == nTable )
540             {
541                 pE->nOffset = (sal_uInt16) (*pLocalColOffset)[pE->nCol - nColCntStart];
542                 pE->nWidth = 0;     // to be recalculated later
543             }
544             pE = pList->Next();
545         }
546     }
547     else
548     {   // einige mit einige ohne Width
549         pE = pList->Seek( nFirstTableCell );
550         // #36350# wieso eigentlich kein pE ?!?
551         if ( pE )
552         {
553             sal_uInt16* pOffsets = new sal_uInt16[ nColsPerRow+1 ];
554             memset( pOffsets, 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
555             sal_uInt16* pWidths = new sal_uInt16[ nColsPerRow ];
556             memset( pWidths, 0, nColsPerRow * sizeof(sal_uInt16) );
557             pOffsets[0] = nColOffsetStart;
558             while ( pE )
559             {
560                 if ( pE->nTab == nTable && pE->nWidth )
561                 {
562                     nCol = pE->nCol - nColCntStart;
563                     if ( nCol < nColsPerRow )
564                     {
565                         if ( pE->nColOverlap == 1 )
566                         {
567                             if ( pWidths[nCol] < pE->nWidth )
568                                 pWidths[nCol] = pE->nWidth;
569                         }
570                         else
571                         {   // try to find a single undefined width
572                             sal_uInt16 nTotal = 0;
573                             sal_Bool bFound = sal_False;
574                             SCCOL nHere = 0;
575                             SCCOL nStop = Min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
576                             for ( ; nCol < nStop; nCol++ )
577                             {
578                                 if ( pWidths[nCol] )
579                                     nTotal = nTotal + pWidths[nCol];
580                                 else
581                                 {
582                                     if ( bFound )
583                                     {
584                                         bFound = sal_False;
585                                         break;  // for
586                                     }
587                                     bFound = sal_True;
588                                     nHere = nCol;
589                                 }
590                             }
591                             if ( bFound && pE->nWidth > nTotal )
592                                 pWidths[nHere] = pE->nWidth - nTotal;
593                         }
594                     }
595                 }
596                 pE = pList->Next();
597             }
598             sal_uInt16 nWidths = 0;
599             sal_uInt16 nUnknown = 0;
600             for ( nCol = 0; nCol < nColsPerRow; nCol++ )
601             {
602                 if ( pWidths[nCol] )
603                     nWidths = nWidths + pWidths[nCol];
604                 else
605                     nUnknown++;
606             }
607             if ( nUnknown )
608             {
609                 sal_uInt16 nW = ((nWidths < nTableWidth) ?
610                     ((nTableWidth - nWidths) / nUnknown) :
611                     (nTableWidth / nUnknown));
612                 for ( nCol = 0; nCol < nColsPerRow; nCol++ )
613                 {
614                     if ( !pWidths[nCol] )
615                         pWidths[nCol] = nW;
616                 }
617             }
618             for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
619             {
620                 pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
621             }
622             pLocalColOffset->Remove( (sal_uInt16)0, pLocalColOffset->Count() );
623             for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
624             {
625                 MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
626             }
627             nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
628 
629             pE = pList->Seek( nFirstTableCell );
630             while ( pE )
631             {
632                 if ( pE->nTab == nTable )
633                 {
634                     nCol = pE->nCol - nColCntStart;
635                     DBG_ASSERT( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
636                     if ( nCol < nColsPerRow )
637                     {
638                         pE->nOffset = pOffsets[nCol];
639                         nCol = nCol + pE->nColOverlap;
640                         if ( nCol > nColsPerRow )
641                             nCol = nColsPerRow;
642                         pE->nWidth = pOffsets[nCol] - pE->nOffset;
643                     }
644                 }
645                 pE = pList->Next();
646             }
647 
648             delete [] pWidths;
649             delete [] pOffsets;
650         }
651     }
652     if ( pLocalColOffset->Count() )
653     {
654         sal_uInt16 nMax = (sal_uInt16) (*pLocalColOffset)[pLocalColOffset->Count() - 1];
655         if ( aPageSize.Width() < nMax )
656             aPageSize.Width() = nMax;
657     }
658     pE = pList->Seek( nFirstTableCell );
659     while ( pE )
660     {
661         if ( pE->nTab == nTable )
662         {
663             if ( !pE->nWidth )
664             {
665                 pE->nWidth = GetWidth( pE );
666                 DBG_ASSERT( pE->nWidth, "SetWidths: pE->nWidth == 0" );
667             }
668             MakeCol( pColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
669         }
670         pE = pList->Next();
671     }
672 }
673 
674 
Colonize(ScEEParseEntry * pE)675 void ScHTMLLayoutParser::Colonize( ScEEParseEntry* pE )
676 {
677     if ( pE->nCol == SCCOL_MAX )
678         pE->nCol = nColCnt;
679     if ( pE->nRow == SCROW_MAX )
680         pE->nRow = nRowCnt;
681     SCCOL nCol = pE->nCol;
682     SkipLocked( pE );       // Spaltenverdraengung nach rechts
683 
684     if ( nCol < pE->nCol )
685     {   // verdraengt
686         nCol = pE->nCol - nColCntStart;
687         SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->Count());
688         if ( nCol < nCount )
689             nColOffset = (sal_uInt16) (*pLocalColOffset)[nCol];
690         else
691             nColOffset = (sal_uInt16) (*pLocalColOffset)[nCount - 1];
692     }
693     pE->nOffset = nColOffset;
694     sal_uInt16 nWidth = GetWidth( pE );
695     MakeCol( pLocalColOffset, pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance );
696     if ( pE->nWidth )
697         pE->nWidth = nWidth;
698     nColOffset = pE->nOffset + nWidth;
699     if ( nTableWidth < nColOffset - nColOffsetStart )
700         nTableWidth = nColOffset - nColOffsetStart;
701 }
702 
703 
CloseEntry(ImportInfo * pInfo)704 void ScHTMLLayoutParser::CloseEntry( ImportInfo* pInfo )
705 {
706     bInCell = sal_False;
707     if ( bTabInTabCell )
708     {   // in TableOff vom Stack geholt
709         bTabInTabCell = sal_False;
710         if ( pList->GetPos( pActEntry ) == LIST_ENTRY_NOTFOUND )
711             delete pActEntry;
712         NewActEntry( pList->Last() );   // neuer freifliegender pActEntry
713         return ;
714     }
715     if ( pActEntry->nTab == 0 )
716         pActEntry->nWidth = (sal_uInt16) aPageSize.Width();
717     Colonize( pActEntry );
718     nColCnt = pActEntry->nCol + pActEntry->nColOverlap;
719     if ( nMaxCol < nColCnt )
720         nMaxCol = nColCnt;          // TableStack MaxCol
721     if ( nColMax < nColCnt )
722         nColMax = nColCnt;      // globales MaxCol fuer ScEEParser GetDimensions!
723     EntryEnd( pActEntry, pInfo->aSelection );
724     ESelection& rSel = pActEntry->aSel;
725     while ( rSel.nStartPara < rSel.nEndPara
726             && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
727     {   // vorgehaengte Leerabsaetze strippen
728         rSel.nStartPara++;
729     }
730     while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
731     {   // angehaengte Leerabsaetze strippen
732         rSel.nEndPara--;
733         rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
734     }
735     if ( rSel.nStartPara > rSel.nEndPara )
736     {   // gibt GPF in CreateTextObject
737         DBG_ERRORFILE( "CloseEntry: EditEngine ESelection Start > End" );
738         rSel.nEndPara = rSel.nStartPara;
739     }
740     if ( rSel.HasRange() )
741         pActEntry->aItemSet.Put( SfxBoolItem( ATTR_LINEBREAK, sal_True ) );
742     pList->Insert( pActEntry, LIST_APPEND );
743     NewActEntry( pActEntry );   // neuer freifliegender pActEntry
744 }
745 
746 
IMPL_LINK(ScHTMLLayoutParser,HTMLImportHdl,ImportInfo *,pInfo)747 IMPL_LINK( ScHTMLLayoutParser, HTMLImportHdl, ImportInfo*, pInfo )
748 {
749 #if defined(erDEBUG) //|| 1
750     static ESelection aDebugSel;
751     static String aDebugStr;
752     static SvFileStream* pDebugStrm = NULL;
753     static sal_uLong nDebugStrmPos = 0;
754     static sal_uLong nDebugCount = 0;
755     static sal_uLong nDebugCountAll = 0;
756     static const sal_Char* sDebugState[15] = {
757                     "RTFIMP_START", "RTFIMP_END",
758                     "RTFIMP_NEXTTOKEN", "RTFIMP_UNKNOWNATTR",
759                     "RTFIMP_SETATTR",
760                     "RTFIMP_INSERTTEXT",
761                     "RTFIMP_INSERTPARA",
762                     "HTMLIMP_START", "HTMLIMP_END",
763                     "HTMLIMP_NEXTTOKEN", "HTMLIMP_UNKNOWNATTR",
764                     "HTMLIMP_SETATTR",
765                     "HTMLIMP_INSERTTEXT",
766                     "HTMLIMP_INSERTPARA", "HTMLIMP_INSERTFIELD"
767     };
768 
769     nDebugCountAll++;
770     if ( pInfo->eState != HTMLIMP_NEXTTOKEN     // not too much
771       || pInfo->nToken == HTML_TABLE_ON
772       || pInfo->nToken == HTML_TABLE_OFF
773       || pInfo->nToken == HTML_TABLEROW_ON
774       || pInfo->nToken == HTML_TABLEROW_OFF
775       || pInfo->nToken == HTML_TABLEHEADER_ON
776       || pInfo->nToken == HTML_TABLEHEADER_OFF
777       || pInfo->nToken == HTML_TABLEDATA_ON
778       || pInfo->nToken == HTML_TABLEDATA_OFF
779       || !aDebugSel.IsEqual( pInfo->aSelection )
780       || pInfo->aText.Len() || aDebugStr != pInfo->aText
781         )
782     {
783         aDebugSel = pInfo->aSelection;
784         aDebugStr = pInfo->aText;
785         nDebugCount++;
786         if ( !pDebugStrm )
787         {
788             pDebugStrm = new SvFileStream( "d:\\erdbghtm.log",
789                 STREAM_WRITE | STREAM_TRUNC );
790         }
791         else
792         {
793             pDebugStrm->ReOpen();
794             pDebugStrm->Seek( nDebugStrmPos );
795         }
796         SvFileStream& rS = *pDebugStrm;
797         rS.WriteNumber( nDebugCountAll ); rS << ".: ";
798         rS.WriteNumber( nDebugCount ); rS << ". State: ";
799         rS.WriteNumber( (sal_uInt16) pInfo->eState );
800         rS << ' ' << sDebugState[pInfo->eState] << endl;
801         rS << "SPar,SPos EPar,EPos: ";
802         rS.WriteNumber( aDebugSel.nStartPara ); rS << ',';
803         rS.WriteNumber( aDebugSel.nStartPos ); rS << ' ';
804         rS.WriteNumber( aDebugSel.nEndPara ); rS << ',';
805         rS.WriteNumber( aDebugSel.nEndPos ); rS << endl;
806         if ( aDebugStr.Len() )
807         {
808             rS << "Text: \"" << aDebugStr << '\"' << endl;
809         }
810         else
811         {
812             rS << "Text:" << endl;
813         }
814         rS << "Token: "; rS.WriteNumber( pInfo->nToken );
815         switch ( pInfo->nToken )
816         {
817             case HTML_TABLE_ON:
818                 rS << " HTML_TABLE_ON";
819                 break;
820             case HTML_TABLE_OFF:
821                 rS << " HTML_TABLE_OFF";
822                 break;
823             case HTML_TABLEROW_ON:
824                 rS << " HTML_TABLEROW_ON";
825                 break;
826             case HTML_TABLEROW_OFF:
827                 rS << " HTML_TABLEROW_OFF";
828                 break;
829             case HTML_TABLEHEADER_ON:
830                 rS << " HTML_TABLEHEADER_ON";
831                 break;
832             case HTML_TABLEHEADER_OFF:
833                 rS << " HTML_TABLEHEADER_OFF";
834                 break;
835             case HTML_TABLEDATA_ON:
836                 rS << " HTML_TABLEDATA_ON";
837                 break;
838             case HTML_TABLEDATA_OFF:
839                 rS << " HTML_TABLEDATA_OFF";
840                 break;
841         }
842         rS << " Value: "; rS.WriteNumber( pInfo->nTokenValue );
843         rS << endl << endl;
844         nDebugStrmPos = pDebugStrm->Tell();
845         pDebugStrm->Close();
846     }
847 #endif
848     switch ( pInfo->eState )
849     {
850         case HTMLIMP_NEXTTOKEN:
851             ProcToken( pInfo );
852             break;
853         case HTMLIMP_UNKNOWNATTR:
854             ProcToken( pInfo );
855             break;
856         case HTMLIMP_START:
857             break;
858         case HTMLIMP_END:
859             if ( pInfo->aSelection.nEndPos )
860             {
861                 // If text remains: create paragraph, without calling CloseEntry().
862                 if( bInCell )   // #108269# ...but only in opened table cells.
863                 {
864                     bInCell = sal_False;
865                     NextRow( pInfo );
866                     bInCell = sal_True;
867                 }
868                 CloseEntry( pInfo );
869             }
870             while ( nTableLevel > 0 )
871                 TableOff( pInfo );      // close tables, if </TABLE> missing
872             break;
873         case HTMLIMP_SETATTR:
874             break;
875         case HTMLIMP_INSERTTEXT:
876             break;
877         case HTMLIMP_INSERTPARA:
878             if ( nTableLevel < 1 )
879             {
880                 CloseEntry( pInfo );
881                 NextRow( pInfo );
882             }
883             break;
884         case HTMLIMP_INSERTFIELD:
885             break;
886         default:
887             DBG_ERRORFILE("HTMLImportHdl: unknown ImportInfo.eState");
888     }
889     return 0;
890 }
891 
892 
893 // Groesster Gemeinsamer Teiler nach Euklid (Kettendivision)
894 // Sonderfall: 0 und irgendwas geben 1
lcl_GGT(SCROW a,SCROW b)895 SCROW lcl_GGT( SCROW a, SCROW b )
896 {
897     if ( !a || !b )
898         return 1;
899     do
900     {
901         if ( a > b )
902             a -= SCROW(a / b) * b;
903         else
904             b -= SCROW(b / a) * a;
905     } while ( a && b );
906     return ((a != 0) ? a : b);
907 }
908 
909 
910 // Kleinstes Gemeinsames Vielfaches: a * b / GGT(a,b)
lcl_KGV(SCROW a,SCROW b)911 SCROW lcl_KGV( SCROW a, SCROW b )
912 {
913     if ( a > b )    // Ueberlauf unwahrscheinlicher machen
914         return (a / lcl_GGT(a,b)) * b;
915     else
916         return (b / lcl_GGT(a,b)) * a;
917 }
918 
919 
TableDataOn(ImportInfo * pInfo)920 void ScHTMLLayoutParser::TableDataOn( ImportInfo* pInfo )
921 {
922     if ( bInCell )
923         CloseEntry( pInfo );
924     if ( !nTableLevel )
925     {
926         DBG_ERROR( "Dummbatz-Dok! <TH> oder <TD> ohne vorheriges <TABLE>" );
927         TableOn( pInfo );
928     }
929     bInCell = sal_True;
930     sal_Bool bHorJustifyCenterTH = (pInfo->nToken == HTML_TABLEHEADER_ON);
931     const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
932     sal_uInt16 nArrLen = pOptions->Count();
933     for ( sal_uInt16 i = 0; i < nArrLen; i++ )
934     {
935         const HTMLOption* pOption = (*pOptions)[i];
936         switch( pOption->GetToken() )
937         {
938             case HTML_O_COLSPAN:
939             {
940                 pActEntry->nColOverlap = ( SCCOL ) pOption->GetString().ToInt32();
941             }
942             break;
943             case HTML_O_ROWSPAN:
944             {
945                 pActEntry->nRowOverlap = ( SCROW ) pOption->GetString().ToInt32();
946             }
947             break;
948             case HTML_O_ALIGN:
949             {
950                 bHorJustifyCenterTH = sal_False;
951                 SvxCellHorJustify eVal;
952                 const String& rOptVal = pOption->GetString();
953                 if ( rOptVal.CompareIgnoreCaseToAscii( OOO_STRING_SVTOOLS_HTML_AL_right ) == COMPARE_EQUAL )
954                     eVal = SVX_HOR_JUSTIFY_RIGHT;
955                 else if ( rOptVal.CompareIgnoreCaseToAscii( OOO_STRING_SVTOOLS_HTML_AL_center ) == COMPARE_EQUAL )
956                     eVal = SVX_HOR_JUSTIFY_CENTER;
957                 else if ( rOptVal.CompareIgnoreCaseToAscii( OOO_STRING_SVTOOLS_HTML_AL_left ) == COMPARE_EQUAL )
958                     eVal = SVX_HOR_JUSTIFY_LEFT;
959                 else
960                     eVal = SVX_HOR_JUSTIFY_STANDARD;
961                 if ( eVal != SVX_HOR_JUSTIFY_STANDARD )
962                     pActEntry->aItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY) );
963             }
964             break;
965             case HTML_O_VALIGN:
966             {
967                 SvxCellVerJustify eVal;
968                 const String& rOptVal = pOption->GetString();
969                 if ( rOptVal.CompareIgnoreCaseToAscii( OOO_STRING_SVTOOLS_HTML_VA_top ) == COMPARE_EQUAL )
970                     eVal = SVX_VER_JUSTIFY_TOP;
971                 else if ( rOptVal.CompareIgnoreCaseToAscii( OOO_STRING_SVTOOLS_HTML_VA_middle ) == COMPARE_EQUAL )
972                     eVal = SVX_VER_JUSTIFY_CENTER;
973                 else if ( rOptVal.CompareIgnoreCaseToAscii( OOO_STRING_SVTOOLS_HTML_VA_bottom ) == COMPARE_EQUAL )
974                     eVal = SVX_VER_JUSTIFY_BOTTOM;
975                 else
976                     eVal = SVX_VER_JUSTIFY_STANDARD;
977                 pActEntry->aItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY) );
978             }
979             break;
980             case HTML_O_WIDTH:
981             {
982                 pActEntry->nWidth = GetWidthPixel( pOption );
983             }
984             break;
985             case HTML_O_BGCOLOR:
986             {
987                 Color aColor;
988                 pOption->GetColor( aColor );
989                 pActEntry->aItemSet.Put(
990                     SvxBrushItem( aColor, ATTR_BACKGROUND ) );
991             }
992             break;
993             case HTML_O_SDVAL:
994             {
995                 pActEntry->pValStr = new String( pOption->GetString() );
996             }
997             break;
998             case HTML_O_SDNUM:
999             {
1000                 pActEntry->pNumStr = new String( pOption->GetString() );
1001             }
1002             break;
1003         }
1004     }
1005     pActEntry->nCol = nColCnt;
1006     pActEntry->nRow = nRowCnt;
1007     pActEntry->nTab = nTable;
1008 
1009     if ( bHorJustifyCenterTH )
1010         pActEntry->aItemSet.Put(
1011             SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY) );
1012 }
1013 
1014 
TableRowOn(ImportInfo * pInfo)1015 void ScHTMLLayoutParser::TableRowOn( ImportInfo* pInfo )
1016 {
1017     if ( nColCnt > nColCntStart )
1018         NextRow( pInfo );       // das optionale TableRowOff war nicht
1019     nColOffset = nColOffsetStart;
1020 }
1021 
1022 
TableRowOff(ImportInfo * pInfo)1023 void ScHTMLLayoutParser::TableRowOff( ImportInfo* pInfo )
1024 {
1025     NextRow( pInfo );
1026 }
1027 
1028 
TableDataOff(ImportInfo * pInfo)1029 void ScHTMLLayoutParser::TableDataOff( ImportInfo* pInfo )
1030 {
1031     if ( bInCell )
1032         CloseEntry( pInfo );    // aber nur wenn's auch eine war
1033 }
1034 
1035 
TableOn(ImportInfo * pInfo)1036 void ScHTMLLayoutParser::TableOn( ImportInfo* pInfo )
1037 {
1038     String aTabName;
1039     bool bBorderOn = false;
1040 
1041     if ( ++nTableLevel > 1 )
1042     {   // Table in Table
1043         sal_uInt16 nTmpColOffset = nColOffset;  // wird in Colonize noch angepasst
1044         Colonize( pActEntry );
1045         aTableStack.Push( new ScHTMLTableStackEntry(
1046             pActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
1047             nColCnt, nRowCnt, nColCntStart, nMaxCol, nTable,
1048             nTableWidth, nColOffset, nColOffsetStart,
1049             bFirstRow ) );
1050         sal_uInt16 nLastWidth = nTableWidth;
1051         nTableWidth = GetWidth( pActEntry );
1052         if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
1053         {   // es muss mehr als einen geben, also kann dieser nicht alles sein
1054             nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
1055         }
1056         nLastWidth = nTableWidth;
1057         if ( pInfo->nToken == HTML_TABLE_ON )
1058         {   // es kann auch TD oder TH sein, wenn es vorher kein TABLE gab
1059             const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
1060             sal_uInt16 nArrLen = pOptions->Count();
1061             for ( sal_uInt16 i = 0; i < nArrLen; i++ )
1062             {
1063                 const HTMLOption* pOption = (*pOptions)[i];
1064                 switch( pOption->GetToken() )
1065                 {
1066                     case HTML_O_WIDTH:
1067                     {   // Prozent: von Dokumentbreite bzw. aeusserer Zelle
1068                         nTableWidth = GetWidthPixel( pOption );
1069                     }
1070                     break;
1071                     case HTML_O_BORDER:
1072                         bBorderOn = ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1073                     break;
1074                     case HTML_O_ID:
1075                         aTabName.Assign( pOption->GetString() );
1076                     break;
1077                 }
1078             }
1079         }
1080         bInCell = sal_False;
1081         if ( bTabInTabCell && !(nTableWidth < nLastWidth) )
1082         {   // mehrere Tabellen in einer Zelle, untereinander
1083             bTabInTabCell = sal_False;
1084             NextRow( pInfo );
1085         }
1086         else
1087         {   // in dieser Zelle geht's los, oder nebeneinander
1088             bTabInTabCell = sal_False;
1089             nColCntStart = nColCnt;
1090             nColOffset = nTmpColOffset;
1091             nColOffsetStart = nColOffset;
1092         }
1093 
1094         ScEEParseEntry* pE = pList->Last();
1095         NewActEntry( pE );      // neuer freifliegender pActEntry
1096         xLockedList = new ScRangeList;
1097     }
1098     else
1099     {   // einfache Table auf Dokumentebene
1100         EntryEnd( pActEntry, pInfo->aSelection );
1101         if ( pActEntry->aSel.HasRange() )
1102         {   // noch fliegender Text
1103             CloseEntry( pInfo );
1104             NextRow( pInfo );
1105         }
1106         aTableStack.Push( new ScHTMLTableStackEntry(
1107             pActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
1108             nColCnt, nRowCnt, nColCntStart, nMaxCol, nTable,
1109             nTableWidth, nColOffset, nColOffsetStart,
1110             bFirstRow ) );
1111         // As soon as we have multiple tables we need to be tolerant with the offsets.
1112         if (nMaxTable > 0)
1113             nOffsetTolerance = SC_HTML_OFFSET_TOLERANCE_LARGE;
1114         nTableWidth = 0;
1115         if ( pInfo->nToken == HTML_TABLE_ON )
1116         {   // es kann auch TD oder TH sein, wenn es vorher kein TABLE gab
1117             const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
1118             sal_uInt16 nArrLen = pOptions->Count();
1119             for ( sal_uInt16 i = 0; i < nArrLen; i++ )
1120             {
1121                 const HTMLOption* pOption = (*pOptions)[i];
1122                 switch( pOption->GetToken() )
1123                 {
1124                     case HTML_O_WIDTH:
1125                     {   // Prozent: von Dokumentbreite bzw. aeusserer Zelle
1126                         nTableWidth = GetWidthPixel( pOption );
1127                     }
1128                     break;
1129                     case HTML_O_BORDER:
1130                         bBorderOn = ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
1131                     break;
1132                     case HTML_O_ID:
1133                         aTabName.Assign( pOption->GetString() );
1134                     break;
1135                 }
1136             }
1137         }
1138     }
1139     nTable = ++nMaxTable;
1140     bFirstRow = sal_True;
1141     nFirstTableCell = pList->Count();
1142 
1143     pLocalColOffset = new ScHTMLColOffset;
1144     MakeColNoRef( pLocalColOffset, nColOffsetStart, 0, 0, 0 );
1145 }
1146 
1147 
TableOff(ImportInfo * pInfo)1148 void ScHTMLLayoutParser::TableOff( ImportInfo* pInfo )
1149 {
1150     if ( bInCell )
1151         CloseEntry( pInfo );
1152     if ( nColCnt > nColCntStart )
1153         TableRowOff( pInfo );      // das optionale TableRowOff war nicht
1154     if ( !nTableLevel )
1155     {
1156         DBG_ERROR( "Dummbatz-Dok! </TABLE> ohne oeffnendes <TABLE>" );
1157         return ;
1158     }
1159     if ( --nTableLevel > 0 )
1160     {   // Table in Table beendet
1161         ScHTMLTableStackEntry* pS = aTableStack.Pop();
1162         if ( pS )
1163         {
1164             ScEEParseEntry* pE = pS->pCellEntry;
1165             SCROW nRows = nRowCnt - pS->nRowCnt;
1166             if ( nRows > 1 )
1167             {   // Groesse der Tabelle an dieser Position eintragen
1168                 SCROW nRow = pS->nRowCnt;
1169                 sal_uInt16 nTab = pS->nTable;
1170                 if ( !pTables )
1171                     pTables = new Table;
1172                 // Hoehen der aeusseren Table
1173                 Table* pTab1 = (Table*) pTables->Get( nTab );
1174                 if ( !pTab1 )
1175                 {
1176                     pTab1 = new Table;
1177                     pTables->Insert( nTab, pTab1 );
1178                 }
1179                 SCROW nRowSpan = pE->nRowOverlap;
1180                 SCROW nRowKGV;
1181                 SCROW nRowsPerRow1;    // aeussere Table
1182                 SCROW nRowsPerRow2;    // innere Table
1183                 if ( nRowSpan > 1 )
1184                 {   // KGV auf das sich aussere und innere Zeilen
1185                     // abbilden lassen
1186                     nRowKGV = lcl_KGV( nRowSpan, nRows );
1187                     nRowsPerRow1 = nRowKGV / nRowSpan;
1188                     nRowsPerRow2 = nRowKGV / nRows;
1189                 }
1190                 else
1191                 {
1192                     nRowKGV = nRowsPerRow1 = nRows;
1193                     nRowsPerRow2 = 1;
1194                 }
1195                 Table* pTab2 = NULL;
1196                 if ( nRowsPerRow2 > 1 )
1197                 {   // Hoehen der inneren Table
1198                     pTab2 = new Table;
1199                     pTables->Insert( nTable, pTab2 );
1200                 }
1201                 // void* Data-Entry der Table-Class fuer das
1202                 // Hoehen-Mapping missbrauchen
1203                 if ( nRowKGV > 1 )
1204                 {
1205                     if ( nRowsPerRow1 > 1 )
1206                     {   // aussen
1207                         for ( SCROW j=0; j < nRowSpan; j++ )
1208                         {
1209                             sal_uLong nRowKey = nRow + j;
1210                             SCROW nR = (SCROW)(sal_uLong)pTab1->Get( nRowKey );
1211                             if ( !nR )
1212                                 pTab1->Insert( nRowKey, (void*) nRowsPerRow1 );
1213                             else if ( nRowsPerRow1 > nR )
1214                                 pTab1->Replace( nRowKey, (void*) nRowsPerRow1 );
1215                                 //2do: wie geht das noch besser?
1216                             else if ( nRowsPerRow1 < nR && nRowSpan == 1
1217                               && nTable == nMaxTable )
1218                             {   // Platz uebrig, evtl. besser mergen
1219                                 SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
1220                                 nR += nAdd;
1221                                 if ( (nR % nRows) == 0 )
1222                                 {   // nur wenn abbildbar
1223                                     SCROW nR2 = (SCROW)(sal_uLong)pTab1->Get( nRowKey+1 );
1224                                     if ( nR2 > nAdd )
1225                                     {   // nur wenn wirklich Platz
1226                                         pTab1->Replace( nRowKey, (void*) nR );
1227                                         pTab1->Replace( nRowKey+1, (void*) (nR2 - nAdd) );
1228                                         nRowsPerRow2 = nR / nRows;
1229                                     }
1230                                 }
1231                             }
1232                         }
1233                     }
1234                     if ( nRowsPerRow2 > 1 )
1235                     {   // innen
1236                         if ( !pTab2 )
1237                         {   // nRowsPerRow2 kann erhoeht worden sein
1238                             pTab2 = new Table;
1239                             pTables->Insert( nTable, pTab2 );
1240                         }
1241                         for ( SCROW j=0; j < nRows; j++ )
1242                         {
1243                             sal_uLong nRowKey = nRow + j;
1244                             SCROW nR = (SCROW)(sal_uLong)pTab2->Get( nRowKey );
1245                             if ( !nR )
1246                                 pTab2->Insert( nRowKey, (void*) nRowsPerRow2 );
1247                             else if ( nRowsPerRow2 > nR )
1248                                 pTab2->Replace( nRowKey, (void*) nRowsPerRow2 );
1249                         }
1250                     }
1251                 }
1252             }
1253 
1254             SetWidths();
1255 
1256             if ( !pE->nWidth )
1257                 pE->nWidth = nTableWidth;
1258             else if ( pE->nWidth < nTableWidth )
1259             {
1260                 sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
1261                 sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
1262                 ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
1263                 sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
1264                 pE->nWidth = nNewOffset - pE->nOffset;
1265                 pS->nTableWidth = pS->nTableWidth + nTmp;
1266                 if ( pS->nColOffset >= nOldOffset )
1267                     pS->nColOffset = pS->nColOffset + nTmp;
1268             }
1269 
1270             nColCnt = pE->nCol + pE->nColOverlap;
1271             nRowCnt = pS->nRowCnt;
1272             nColCntStart = pS->nColCntStart;
1273             nMaxCol = pS->nMaxCol;
1274             nTable = pS->nTable;
1275             nTableWidth = pS->nTableWidth;
1276             nFirstTableCell = pS->nFirstTableCell;
1277             nColOffset = pS->nColOffset;
1278             nColOffsetStart = pS->nColOffsetStart;
1279             bFirstRow = pS->bFirstRow;
1280             xLockedList = pS->xLockedList;
1281             if ( pLocalColOffset )
1282                 delete pLocalColOffset;
1283             pLocalColOffset = pS->pLocalColOffset;
1284             delete pActEntry;
1285             // pActEntry bleibt erstmal erhalten falls da noch 'ne Table in
1286             // der gleichen Zelle aufgemacht werden sollte (in HTML ist ja
1287             // alles moeglich..) und wird in CloseEntry deleted
1288             pActEntry = pE;
1289             delete pS;
1290         }
1291         bTabInTabCell = sal_True;
1292         bInCell = sal_True;
1293     }
1294     else
1295     {   // einfache Table beendet
1296         SetWidths();
1297         ScHTMLTableStackEntry* pS = aTableStack.Pop();
1298         nMaxCol = 0;
1299         nTable = 0;
1300         if ( pS )
1301         {
1302             if ( pLocalColOffset )
1303                 delete pLocalColOffset;
1304             pLocalColOffset = pS->pLocalColOffset;
1305             delete pS;
1306         }
1307     }
1308 }
1309 
1310 
Image(ImportInfo * pInfo)1311 void ScHTMLLayoutParser::Image( ImportInfo* pInfo )
1312 {
1313     if ( !pActEntry->pImageList )
1314         pActEntry->pImageList = new ScHTMLImageList;
1315     ScHTMLImageList* pIL = pActEntry->pImageList;
1316     ScHTMLImage* pImage = new ScHTMLImage;
1317     pIL->Insert( pImage, LIST_APPEND );
1318     const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
1319     sal_uInt16 nArrLen = pOptions->Count();
1320     for ( sal_uInt16 i = 0; i < nArrLen; i++ )
1321     {
1322         const HTMLOption* pOption = (*pOptions)[i];
1323         switch( pOption->GetToken() )
1324         {
1325             case HTML_O_SRC:
1326             {
1327                 pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, pOption->GetString() );
1328             }
1329             break;
1330             case HTML_O_ALT:
1331             {
1332                 if ( !pActEntry->bHasGraphic )
1333                 {   // ALT text only if not any image loaded
1334                     if ( pActEntry->aAltText.Len() )
1335                         pActEntry->aAltText.AppendAscii( "; " );
1336                     pActEntry->aAltText += pOption->GetString();
1337                 }
1338             }
1339             break;
1340             case HTML_O_WIDTH:
1341             {
1342                 pImage->aSize.Width() = (long)pOption->GetNumber();
1343             }
1344             break;
1345             case HTML_O_HEIGHT:
1346             {
1347                 pImage->aSize.Height() = (long)pOption->GetNumber();
1348             }
1349             break;
1350             case HTML_O_HSPACE:
1351             {
1352                 pImage->aSpace.X() = (long)pOption->GetNumber();
1353             }
1354             break;
1355             case HTML_O_VSPACE:
1356             {
1357                 pImage->aSpace.Y() = (long)pOption->GetNumber();
1358             }
1359             break;
1360         }
1361     }
1362     if ( !pImage->aURL.Len() )
1363     {
1364         DBG_ERRORFILE( "Image: Grafik ohne URL ?!?" );
1365         return ;
1366     }
1367 
1368     sal_uInt16 nFormat;
1369     Graphic* pGraphic = new Graphic;
1370     GraphicFilter* pFilter = GraphicFilter::GetGraphicFilter();
1371     if ( GRFILTER_OK != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
1372             *pGraphic, pFilter, &nFormat ) )
1373     {
1374         delete pGraphic;
1375         return ;        // dumm gelaufen
1376     }
1377     if ( !pActEntry->bHasGraphic )
1378     {   // discard any ALT text in this cell if we have any image
1379         pActEntry->bHasGraphic = sal_True;
1380         pActEntry->aAltText.Erase();
1381     }
1382     pImage->aFilterName = pFilter->GetImportFormatName( nFormat );
1383     pImage->pGraphic = pGraphic;
1384     if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
1385     {
1386         OutputDevice* pDefaultDev = Application::GetDefaultDevice();
1387         pImage->aSize = pDefaultDev->LogicToPixel( pGraphic->GetPrefSize(),
1388             pGraphic->GetPrefMapMode() );
1389     }
1390     if ( pIL->Count() > 0 )
1391     {
1392         long nWidth = 0;
1393         for ( ScHTMLImage* pI = pIL->First(); pI; pI = pIL->Next() )
1394         {
1395             if ( pI->nDir & nHorizontal )
1396                 nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
1397             else
1398                 nWidth = 0;
1399         }
1400         if ( pActEntry->nWidth
1401           && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
1402                 >= pActEntry->nWidth) )
1403             pIL->Last()->nDir = nVertical;
1404     }
1405 }
1406 
1407 
ColOn(ImportInfo * pInfo)1408 void ScHTMLLayoutParser::ColOn( ImportInfo* pInfo )
1409 {
1410     const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
1411     sal_uInt16 nArrLen = pOptions->Count();
1412     for ( sal_uInt16 i = 0; i < nArrLen; i++ )
1413     {
1414         const HTMLOption* pOption = (*pOptions)[i];
1415         switch( pOption->GetToken() )
1416         {
1417             case HTML_O_WIDTH:
1418             {
1419                 sal_uInt16 nVal = GetWidthPixel( pOption );
1420                 MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
1421                 nColOffset = nColOffset + nVal;
1422             }
1423             break;
1424         }
1425     }
1426 }
1427 
1428 
GetWidthPixel(const HTMLOption * pOption)1429 sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption* pOption )
1430 {
1431     const String& rOptVal = pOption->GetString();
1432     if ( rOptVal.Search('%') != STRING_NOTFOUND )
1433     {   // Prozent
1434         sal_uInt16 nW = (nTableWidth ? nTableWidth : (sal_uInt16) aPageSize.Width());
1435         return (sal_uInt16)((pOption->GetNumber() * nW) / 100);
1436     }
1437     else
1438     {
1439         if ( rOptVal.Search('*') != STRING_NOTFOUND )
1440         {   // relativ zu was?!?
1441 //2do: ColArray aller relativen Werte sammeln und dann MakeCol
1442             return 0;
1443         }
1444         else
1445             return (sal_uInt16)pOption->GetNumber();    // Pixel
1446     }
1447 }
1448 
1449 
AnchorOn(ImportInfo * pInfo)1450 void ScHTMLLayoutParser::AnchorOn( ImportInfo* pInfo )
1451 {
1452     const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
1453     sal_uInt16 nArrLen = pOptions->Count();
1454     for ( sal_uInt16 i = 0; i < nArrLen; i++ )
1455     {
1456         const HTMLOption* pOption = (*pOptions)[i];
1457         switch( pOption->GetToken() )
1458         {
1459             case HTML_O_NAME:
1460             {
1461                 pActEntry->pName = new String( pOption->GetString() );
1462             }
1463             break;
1464         }
1465     }
1466 }
1467 
1468 
IsAtBeginningOfText(ImportInfo * pInfo)1469 sal_Bool ScHTMLLayoutParser::IsAtBeginningOfText( ImportInfo* pInfo )
1470 {
1471     ESelection& rSel = pActEntry->aSel;
1472     return rSel.nStartPara == rSel.nEndPara &&
1473         rSel.nStartPara <= pInfo->aSelection.nEndPara &&
1474         pEdit->GetTextLen( rSel.nStartPara ) == 0;
1475 }
1476 
1477 
FontOn(ImportInfo * pInfo)1478 void ScHTMLLayoutParser::FontOn( ImportInfo* pInfo )
1479 {
1480     if ( IsAtBeginningOfText( pInfo ) )
1481     {   // nur am Anfang des Textes, gilt dann fuer gesamte Zelle
1482         const HTMLOptions* pOptions = ((HTMLParser*)pInfo->pParser)->GetOptions();
1483         sal_uInt16 nArrLen = pOptions->Count();
1484         for ( sal_uInt16 i = 0; i < nArrLen; i++ )
1485         {
1486             const HTMLOption* pOption = (*pOptions)[i];
1487             switch( pOption->GetToken() )
1488             {
1489                 case HTML_O_FACE :
1490                 {
1491                     const String& rFace = pOption->GetString();
1492                     String aFontName;
1493                     xub_StrLen nPos = 0;
1494                     while( nPos != STRING_NOTFOUND )
1495                     {   // Fontliste, VCL: Semikolon als Separator, HTML: Komma
1496                         String aFName = rFace.GetToken( 0, ',', nPos );
1497                         aFName.EraseTrailingChars().EraseLeadingChars();
1498                         if( aFontName.Len() )
1499                             aFontName += ';';
1500                         aFontName += aFName;
1501                     }
1502                     if ( aFontName.Len() )
1503                         pActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
1504                             aFontName, EMPTY_STRING, PITCH_DONTKNOW,
1505                             RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
1506                 }
1507                 break;
1508                 case HTML_O_SIZE :
1509                 {
1510                     sal_uInt16 nSize = (sal_uInt16) pOption->GetNumber();
1511                     if ( nSize == 0 )
1512                         nSize = 1;
1513                     else if ( nSize > SC_HTML_FONTSIZES )
1514                         nSize = SC_HTML_FONTSIZES;
1515                     pActEntry->aItemSet.Put( SvxFontHeightItem(
1516                         maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
1517                 }
1518                 break;
1519                 case HTML_O_COLOR :
1520                 {
1521                     Color aColor;
1522                     pOption->GetColor( aColor );
1523                     pActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
1524                 }
1525                 break;
1526             }
1527         }
1528     }
1529 }
1530 
1531 
ProcToken(ImportInfo * pInfo)1532 void ScHTMLLayoutParser::ProcToken( ImportInfo* pInfo )
1533 {
1534     sal_Bool bSetLastToken = sal_True;
1535     switch ( pInfo->nToken )
1536     {
1537         case HTML_META:
1538         {
1539             HTMLParser* pParser = (HTMLParser*) pInfo->pParser;
1540             uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1541                 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
1542             pParser->ParseMetaOptions(
1543                 xDPS->getDocumentProperties(),
1544                 mpDoc->GetDocumentShell()->GetHeaderAttributes() );
1545         }
1546         break;
1547         case HTML_TITLE_ON:
1548         {
1549             bInTitle = sal_True;
1550             aString.Erase();
1551         }
1552         break;
1553         case HTML_TITLE_OFF:
1554         {
1555             if ( bInTitle && aString.Len() )
1556             {
1557                 // Leerzeichen von Zeilenumbruechen raus
1558                 aString.EraseLeadingChars();
1559                 aString.EraseTrailingChars();
1560                 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1561                     mpDoc->GetDocumentShell()->GetModel(),
1562                     uno::UNO_QUERY_THROW);
1563                 xDPS->getDocumentProperties()->setTitle(aString);
1564             }
1565             bInTitle = sal_False;
1566         }
1567         break;
1568         case HTML_TABLE_ON:
1569         {
1570             TableOn( pInfo );
1571         }
1572         break;
1573         case HTML_COL_ON:
1574         {
1575             ColOn( pInfo );
1576         }
1577         break;
1578         case HTML_TABLEHEADER_ON:       // oeffnet Zelle
1579         {
1580             if ( bInCell )
1581                 CloseEntry( pInfo );
1582             // bInCell nicht sal_True setzen, das macht TableDataOn
1583             pActEntry->aItemSet.Put(
1584                 SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT) );
1585         }   // fall thru
1586         case HTML_TABLEDATA_ON:         // oeffnet Zelle
1587         {
1588             TableDataOn( pInfo );
1589         }
1590         break;
1591         case HTML_TABLEHEADER_OFF:
1592         case HTML_TABLEDATA_OFF:        // schliesst Zelle
1593         {
1594             TableDataOff( pInfo );
1595         }
1596         break;
1597         case HTML_TABLEROW_ON:          // vor erster Zelle in Row
1598         {
1599             TableRowOn( pInfo );
1600         }
1601         break;
1602         case HTML_TABLEROW_OFF:         // nach letzter Zelle in Row
1603         {
1604             TableRowOff( pInfo );
1605         }
1606         break;
1607         case HTML_TABLE_OFF:
1608         {
1609             TableOff( pInfo );
1610         }
1611         break;
1612         case HTML_IMAGE:
1613         {
1614             Image( pInfo );
1615         }
1616         break;
1617         case HTML_PARABREAK_OFF:
1618         {   // nach einem Image geht es vertikal weiter
1619             if ( pActEntry->pImageList && pActEntry->pImageList->Count() > 0 )
1620                 pActEntry->pImageList->Last()->nDir = nVertical;
1621         }
1622         break;
1623         case HTML_ANCHOR_ON:
1624         {
1625             AnchorOn( pInfo );
1626         }
1627         break;
1628         case HTML_FONT_ON :
1629         {
1630             FontOn( pInfo );
1631         }
1632         break;
1633         case HTML_BIGPRINT_ON :
1634         {
1635 //2do: aktuelle Fontgroesse merken und einen groesser
1636             if ( IsAtBeginningOfText( pInfo ) )
1637                 pActEntry->aItemSet.Put( SvxFontHeightItem(
1638                     maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
1639         }
1640         break;
1641         case HTML_SMALLPRINT_ON :
1642         {
1643 //2do: aktuelle Fontgroesse merken und einen kleiner
1644             if ( IsAtBeginningOfText( pInfo ) )
1645                 pActEntry->aItemSet.Put( SvxFontHeightItem(
1646                     maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
1647         }
1648         break;
1649         case HTML_BOLD_ON :
1650         case HTML_STRONG_ON :
1651         {
1652             if ( IsAtBeginningOfText( pInfo ) )
1653                 pActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1654                     ATTR_FONT_WEIGHT ) );
1655         }
1656         break;
1657         case HTML_ITALIC_ON :
1658         case HTML_EMPHASIS_ON :
1659         case HTML_ADDRESS_ON :
1660         case HTML_BLOCKQUOTE_ON :
1661         case HTML_BLOCKQUOTE30_ON :
1662         case HTML_CITIATION_ON :
1663         case HTML_VARIABLE_ON :
1664         {
1665             if ( IsAtBeginningOfText( pInfo ) )
1666                 pActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1667                     ATTR_FONT_POSTURE ) );
1668         }
1669         break;
1670         case HTML_DEFINSTANCE_ON :
1671         {
1672             if ( IsAtBeginningOfText( pInfo ) )
1673             {
1674                 pActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
1675                     ATTR_FONT_WEIGHT ) );
1676                 pActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
1677                     ATTR_FONT_POSTURE ) );
1678             }
1679         }
1680         break;
1681         case HTML_UNDERLINE_ON :
1682         {
1683             if ( IsAtBeginningOfText( pInfo ) )
1684                 pActEntry->aItemSet.Put( SvxUnderlineItem( UNDERLINE_SINGLE,
1685                     ATTR_FONT_UNDERLINE ) );
1686         }
1687         break;
1688         case HTML_TEXTTOKEN:
1689         {
1690             if ( bInTitle )
1691                 aString += pInfo->aText;
1692         }
1693         break;
1694         default:
1695         {   // nLastToken nicht setzen!
1696             bSetLastToken = sal_False;
1697         }
1698     }
1699     if ( bSetLastToken )
1700         nLastToken = pInfo->nToken;
1701 }
1702 
1703 
1704 
1705 // ============================================================================
1706 // HTML DATA QUERY PARSER
1707 // ============================================================================
1708 
1709 template< typename Type >
getLimitedValue(const Type & rValue,const Type & rMin,const Type & rMax)1710 inline Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
1711 { return ::std::max( ::std::min( rValue, rMax ), rMin ); }
1712 
1713 // ============================================================================
1714 
1715 /** Iterates through all HTML tag options of the passed ImportInfo struct. */
1716 class ScHTMLOptionIterator
1717 {
1718 private:
1719     const HTMLOptions*  mpOptions;      /// The options array.
1720     const HTMLOption*   mpCurrOption;   /// Current option.
1721     sal_uInt16          mnCount;        /// Size of the options array.
1722     sal_uInt16          mnIndex;        /// Next option to return.
1723 
1724 public:
1725     explicit            ScHTMLOptionIterator( const ImportInfo& rInfo );
1726 
is() const1727     inline bool         is() const { return mnIndex < mnCount; }
operator ->() const1728     inline const HTMLOption* operator->() const { return mpCurrOption; }
operator *() const1729     inline const HTMLOption& operator*() const { return *mpCurrOption; }
1730     ScHTMLOptionIterator& operator++();
1731 };
1732 
1733 // ----------------------------------------------------------------------------
1734 
ScHTMLOptionIterator(const ImportInfo & rInfo)1735 ScHTMLOptionIterator::ScHTMLOptionIterator( const ImportInfo& rInfo ) :
1736     mpOptions( 0 ),
1737     mpCurrOption( 0 ),
1738     mnCount( 0 ),
1739     mnIndex( 0 )
1740 {
1741     const HTMLParser* pParser = static_cast< const HTMLParser* >( rInfo.pParser );
1742     if( pParser )
1743         mpOptions = pParser->GetOptions();
1744     if( mpOptions )
1745         mnCount = mpOptions->Count();
1746     if( mnCount )
1747         mpCurrOption = mpOptions->GetObject( 0 );
1748 }
1749 
operator ++()1750 ScHTMLOptionIterator& ScHTMLOptionIterator::operator++()
1751 {
1752     if( mnIndex < mnCount ) ++mnIndex;
1753     mpCurrOption = (mnIndex < mnCount) ? mpOptions->GetObject( mnIndex ) : 0;
1754     return *this;
1755 }
1756 
1757 // ============================================================================
1758 
ScHTMLEntry(const SfxItemSet & rItemSet,ScHTMLTableId nTableId)1759 ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
1760     ScEEParseEntry( rItemSet ),
1761     mbImportAlways( false )
1762 {
1763     nTab = nTableId;
1764     bEntirePara = false;
1765 }
1766 
HasContents() const1767 bool ScHTMLEntry::HasContents() const
1768 {
1769      return mbImportAlways || aSel.HasRange() || aAltText.Len() || IsTable();
1770 }
1771 
AdjustStart(const ImportInfo & rInfo)1772 void ScHTMLEntry::AdjustStart( const ImportInfo& rInfo )
1773 {
1774     // set start position
1775     aSel.nStartPara = rInfo.aSelection.nStartPara;
1776     aSel.nStartPos = rInfo.aSelection.nStartPos;
1777     // adjust end position
1778     if( (aSel.nEndPara < aSel.nStartPara) || ((aSel.nEndPara == aSel.nStartPara) && (aSel.nEndPos < aSel.nStartPos)) )
1779     {
1780         aSel.nEndPara = aSel.nStartPara;
1781         aSel.nEndPos = aSel.nStartPos;
1782     }
1783 }
1784 
AdjustEnd(const ImportInfo & rInfo)1785 void ScHTMLEntry::AdjustEnd( const ImportInfo& rInfo )
1786 {
1787     DBG_ASSERT( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
1788                 ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
1789                 "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
1790     // set end position
1791     aSel.nEndPara = rInfo.aSelection.nEndPara;
1792     aSel.nEndPos = rInfo.aSelection.nEndPos;
1793 }
1794 
Strip(const EditEngine & rEditEngine)1795 void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
1796 {
1797     // strip leading empty paragraphs
1798     while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
1799     {
1800         ++aSel.nStartPara;
1801         aSel.nStartPos = 0;
1802     }
1803     // strip trailing empty paragraphs
1804     while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
1805     {
1806         --aSel.nEndPara;
1807         aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
1808     }
1809 }
1810 
1811 // ============================================================================
1812 
1813 /** A map of ScHTMLTable objects.
1814 
1815     Organizes the tables with a unique table key. Stores nested tables inside
1816     the parent table and forms in this way a tree structure of tables. An
1817     instance of this class ownes the contained table objects and deletes them
1818     on destruction.
1819  */
1820 class ScHTMLTableMap
1821 {
1822 private:
1823     typedef ::boost::shared_ptr< ScHTMLTable >          ScHTMLTablePtr;
1824     typedef ::std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
1825 
1826 public:
1827     typedef ScHTMLTableStdMap::iterator             iterator;
1828     typedef ScHTMLTableStdMap::const_iterator       const_iterator;
1829 
1830 private:
1831     ScHTMLTable&        mrParentTable;      /// Reference to parent table.
1832     ScHTMLTableStdMap   maTables;           /// Container for all table objects.
1833     mutable ScHTMLTable* mpCurrTable;       /// Current table, used for fast search.
1834 
1835 public:
1836     explicit            ScHTMLTableMap( ScHTMLTable& rParentTable );
1837     virtual             ~ScHTMLTableMap();
1838 
begin()1839     inline iterator     begin() { return maTables.begin(); }
begin() const1840     inline const_iterator begin() const { return maTables.begin(); }
end()1841     inline iterator     end() { return maTables.end(); }
end() const1842     inline const_iterator end() const { return maTables.end(); }
empty() const1843     inline bool         empty() const { return maTables.empty(); }
1844 
1845     /** Returns the specified table.
1846         @param nTableId  Unique identifier of the table.
1847         @param bDeep  true = searches deep in all nested table; false = only in this container. */
1848     ScHTMLTable*        FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
1849 
1850     /** Inserts a new table into the container. This container owns the created table.
1851         @param bPreFormText  true = New table is based on preformatted text (<pre> tag). */
1852     ScHTMLTable*        CreateTable( const ImportInfo& rInfo, bool bPreFormText );
1853 
1854 private:
1855     /** Sets a working table with its index for search optimization. */
SetCurrTable(ScHTMLTable * pTable) const1856     inline void         SetCurrTable( ScHTMLTable* pTable ) const
1857                             { if( pTable ) mpCurrTable = pTable; }
1858 };
1859 
1860 // ----------------------------------------------------------------------------
1861 
ScHTMLTableMap(ScHTMLTable & rParentTable)1862 ScHTMLTableMap::ScHTMLTableMap( ScHTMLTable& rParentTable ) :
1863     mrParentTable( rParentTable )
1864 {
1865 }
1866 
~ScHTMLTableMap()1867 ScHTMLTableMap::~ScHTMLTableMap()
1868 {
1869 }
1870 
FindTable(ScHTMLTableId nTableId,bool bDeep) const1871 ScHTMLTable* ScHTMLTableMap::FindTable( ScHTMLTableId nTableId, bool bDeep ) const
1872 {
1873     ScHTMLTable* pResult = 0;
1874     if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
1875         pResult = mpCurrTable;              // cached table
1876     else
1877     {
1878         const_iterator aFind = maTables.find( nTableId );
1879         if( aFind != maTables.end() )
1880             pResult = aFind->second.get();  // table from this container
1881     }
1882 
1883     // not found -> search deep in nested tables
1884     if( !pResult && bDeep )
1885         for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
1886             pResult = aIter->second->FindNestedTable( nTableId );
1887 
1888     SetCurrTable( pResult );
1889     return pResult;
1890 }
1891 
CreateTable(const ImportInfo & rInfo,bool bPreFormText)1892 ScHTMLTable* ScHTMLTableMap::CreateTable( const ImportInfo& rInfo, bool bPreFormText )
1893 {
1894     ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText );
1895     maTables[ pTable->GetTableId() ].reset( pTable );
1896     SetCurrTable( pTable );
1897     return pTable;
1898 }
1899 
1900 // ============================================================================
1901 
ScHTMLTableAutoId(ScHTMLTableId & rnUnusedId)1902 ScHTMLTableAutoId::ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId ) :
1903     mnTableId( rnUnusedId ),
1904     mrnUnusedId( rnUnusedId )
1905 {
1906     ++mrnUnusedId;
1907 }
1908 
1909 // ----------------------------------------------------------------------------
1910 
ScHTMLTable(ScHTMLTable & rParentTable,const ImportInfo & rInfo,bool bPreFormText)1911 ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const ImportInfo& rInfo, bool bPreFormText ) :
1912     mpParentTable( &rParentTable ),
1913     maTableId( rParentTable.maTableId.mrnUnusedId ),
1914     maTableItemSet( rParentTable.GetCurrItemSet() ),
1915     mrEditEngine( rParentTable.mrEditEngine ),
1916     mrEEParseList( rParentTable.mrEEParseList ),
1917     mpCurrEntryList( 0 ),
1918     maSize( 1, 1 ),
1919     mbBorderOn( false ),
1920     mbPreFormText( bPreFormText ),
1921     mbRowOn( false ),
1922     mbDataOn( false ),
1923     mbPushEmptyLine( false )
1924 {
1925     if( mbPreFormText )
1926     {
1927         ImplRowOn();
1928         ImplDataOn( ScHTMLSize( 1, 1 ) );
1929     }
1930     else
1931     {
1932         ProcessFormatOptions( maTableItemSet, rInfo );
1933         for( ScHTMLOptionIterator aIter( rInfo ); aIter.is(); ++aIter )
1934         {
1935             switch( aIter->GetToken() )
1936             {
1937                 case HTML_O_BORDER:
1938                     mbBorderOn = ((aIter->GetString().Len() == 0) || (aIter->GetNumber() != 0));
1939                 break;
1940                 case HTML_O_ID:
1941                     maTableName = aIter->GetString();
1942                 break;
1943             }
1944         }
1945     }
1946 
1947     CreateNewEntry( rInfo );
1948 }
1949 
ScHTMLTable(SfxItemPool & rPool,EditEngine & rEditEngine,ScEEParseList & rEEParseList,ScHTMLTableId & rnUnusedId)1950 ScHTMLTable::ScHTMLTable( SfxItemPool& rPool, EditEngine& rEditEngine, ScEEParseList& rEEParseList, ScHTMLTableId& rnUnusedId ) :
1951     mpParentTable( 0 ),
1952     maTableId( rnUnusedId ),
1953     maTableItemSet( rPool ),
1954     mrEditEngine( rEditEngine ),
1955     mrEEParseList( rEEParseList ),
1956     mpCurrEntryList( 0 ),
1957     maSize( 1, 1 ),
1958     mbBorderOn( false ),
1959     mbPreFormText( false ),
1960     mbRowOn( false ),
1961     mbDataOn( false ),
1962     mbPushEmptyLine( false )
1963 {
1964     // open the first "cell" of the document
1965     ImplRowOn();
1966     ImplDataOn( ScHTMLSize( 1, 1 ) );
1967     mxCurrEntry = CreateEntry();
1968 }
1969 
~ScHTMLTable()1970 ScHTMLTable::~ScHTMLTable()
1971 {
1972 }
1973 
GetCurrItemSet() const1974 const SfxItemSet& ScHTMLTable::GetCurrItemSet() const
1975 {
1976     // first try cell item set, then row item set, then table item set
1977     return mxDataItemSet.get() ? *mxDataItemSet : (mxRowItemSet.get() ? *mxRowItemSet : maTableItemSet);
1978 }
1979 
GetSpan(const ScHTMLPos & rCellPos) const1980 ScHTMLSize ScHTMLTable::GetSpan( const ScHTMLPos& rCellPos ) const
1981 {
1982     ScHTMLSize aSpan( 1, 1 );
1983     ScRange* pRange = 0;
1984     if( ((pRange = maVMergedCells.Find( rCellPos.MakeAddr() )) != 0) || ((pRange = maHMergedCells.Find( rCellPos.MakeAddr() )) != 0) )
1985         aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
1986     return aSpan;
1987 }
1988 
FindNestedTable(ScHTMLTableId nTableId) const1989 ScHTMLTable* ScHTMLTable::FindNestedTable( ScHTMLTableId nTableId ) const
1990 {
1991     return mxNestedTables.get() ? mxNestedTables->FindTable( nTableId, true ) : 0;
1992 }
1993 
PutItem(const SfxPoolItem & rItem)1994 void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
1995 {
1996     DBG_ASSERT( mxCurrEntry.get(), "ScHTMLTable::PutItem - no current entry" );
1997     if( mxCurrEntry.get() && mxCurrEntry->IsEmpty() )
1998         mxCurrEntry->GetItemSet().Put( rItem );
1999 }
2000 
PutText(const ImportInfo & rInfo)2001 void ScHTMLTable::PutText( const ImportInfo& rInfo )
2002 {
2003     DBG_ASSERT( mxCurrEntry.get(), "ScHTMLTable::PutText - no current entry" );
2004     if( mxCurrEntry.get() )
2005     {
2006         if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
2007             mxCurrEntry->AdjustStart( rInfo );
2008         else
2009             mxCurrEntry->AdjustEnd( rInfo );
2010     }
2011 }
2012 
InsertPara(const ImportInfo & rInfo)2013 void ScHTMLTable::InsertPara( const ImportInfo& rInfo )
2014 {
2015     if( mxCurrEntry.get() && mbDataOn && !IsEmptyCell() )
2016         mxCurrEntry->SetImportAlways();
2017     PushEntry( rInfo );
2018     CreateNewEntry( rInfo );
2019     InsertLeadingEmptyLine();
2020 }
2021 
BreakOn()2022 void ScHTMLTable::BreakOn()
2023 {
2024     // empty line, if <br> is at start of cell
2025     mbPushEmptyLine = !mbPreFormText && mbDataOn && IsEmptyCell();
2026 }
2027 
HeadingOn()2028 void ScHTMLTable::HeadingOn()
2029 {
2030     // call directly, InsertPara() has not been called before
2031     InsertLeadingEmptyLine();
2032 }
2033 
InsertLeadingEmptyLine()2034 void ScHTMLTable::InsertLeadingEmptyLine()
2035 {
2036     // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
2037     mbPushEmptyLine = !mbPreFormText && mbDataOn && !IsEmptyCell();
2038 }
2039 
AnchorOn()2040 void ScHTMLTable::AnchorOn()
2041 {
2042     DBG_ASSERT( mxCurrEntry.get(), "ScHTMLTable::AnchorOn - no current entry" );
2043     // don't skip entries with single hyperlinks
2044     if( mxCurrEntry.get() )
2045         mxCurrEntry->SetImportAlways();
2046 }
2047 
TableOn(const ImportInfo & rInfo)2048 ScHTMLTable* ScHTMLTable::TableOn( const ImportInfo& rInfo )
2049 {
2050     PushEntry( rInfo );
2051     return InsertNestedTable( rInfo, false );
2052 }
2053 
TableOff(const ImportInfo & rInfo)2054 ScHTMLTable* ScHTMLTable::TableOff( const ImportInfo& rInfo )
2055 {
2056     return mbPreFormText ? this : CloseTable( rInfo );
2057 }
2058 
PreOn(const ImportInfo & rInfo)2059 ScHTMLTable* ScHTMLTable::PreOn( const ImportInfo& rInfo )
2060 {
2061     PushEntry( rInfo );
2062     return InsertNestedTable( rInfo, true );
2063 }
2064 
PreOff(const ImportInfo & rInfo)2065 ScHTMLTable* ScHTMLTable::PreOff( const ImportInfo& rInfo )
2066 {
2067     return mbPreFormText ? CloseTable( rInfo ) : this;
2068 }
2069 
RowOn(const ImportInfo & rInfo)2070 void ScHTMLTable::RowOn( const ImportInfo& rInfo )
2071 {
2072     PushEntry( rInfo, true );
2073     if( mpParentTable && !mbPreFormText )   // no rows allowed in global and preformatted tables
2074     {
2075         ImplRowOn();
2076         ProcessFormatOptions( *mxRowItemSet, rInfo );
2077     }
2078     CreateNewEntry( rInfo );
2079 }
2080 
RowOff(const ImportInfo & rInfo)2081 void ScHTMLTable::RowOff( const ImportInfo& rInfo )
2082 {
2083     PushEntry( rInfo, true );
2084     if( mpParentTable && !mbPreFormText )   // no rows allowed in global and preformatted tables
2085         ImplRowOff();
2086     CreateNewEntry( rInfo );
2087 }
2088 
DataOn(const ImportInfo & rInfo)2089 void ScHTMLTable::DataOn( const ImportInfo& rInfo )
2090 {
2091     PushEntry( rInfo, true );
2092     if( mpParentTable && !mbPreFormText )   // no cells allowed in global and preformatted tables
2093     {
2094         // read needed options from the <td> tag
2095         ScHTMLSize aSpanSize( 1, 1 );
2096         ::std::auto_ptr< String > pValStr, pNumStr;
2097         for( ScHTMLOptionIterator aIter( rInfo ); aIter.is(); ++aIter )
2098         {
2099             switch( aIter->GetToken() )
2100             {
2101                 case HTML_O_COLSPAN:
2102                     aSpanSize.mnCols = static_cast< SCCOL >( getLimitedValue< sal_Int32 >( aIter->GetString().ToInt32(), 1, 256 ) );
2103                 break;
2104                 case HTML_O_ROWSPAN:
2105                     aSpanSize.mnRows = static_cast< SCROW >( getLimitedValue< sal_Int32 >( aIter->GetString().ToInt32(), 1, 256 ) );
2106                 break;
2107                 case HTML_O_SDVAL:
2108                     pValStr.reset( new String( aIter->GetString() ) );
2109                 break;
2110                 case HTML_O_SDNUM:
2111                     pNumStr.reset( new String( aIter->GetString() ) );
2112                 break;
2113             }
2114         }
2115 
2116         ImplDataOn( aSpanSize );
2117         ProcessFormatOptions( *mxDataItemSet, rInfo );
2118         CreateNewEntry( rInfo );
2119         mxCurrEntry->pValStr = pValStr.release();
2120         mxCurrEntry->pNumStr = pNumStr.release();
2121     }
2122     else
2123         CreateNewEntry( rInfo );
2124 }
2125 
DataOff(const ImportInfo & rInfo)2126 void ScHTMLTable::DataOff( const ImportInfo& rInfo )
2127 {
2128     PushEntry( rInfo, true );
2129     if( mpParentTable && !mbPreFormText )   // no cells allowed in global and preformatted tables
2130         ImplDataOff();
2131     CreateNewEntry( rInfo );
2132 }
2133 
BodyOn(const ImportInfo & rInfo)2134 void ScHTMLTable::BodyOn( const ImportInfo& rInfo )
2135 {
2136     bool bPushed = PushEntry( rInfo );
2137     if( !mpParentTable )
2138     {
2139         // #108269# do not start new row, if nothing (no title) precedes the body.
2140         if( bPushed || !mbRowOn )
2141             ImplRowOn();
2142         if( bPushed || !mbDataOn )
2143             ImplDataOn( ScHTMLSize( 1, 1 ) );
2144         ProcessFormatOptions( *mxDataItemSet, rInfo );
2145     }
2146     CreateNewEntry( rInfo );
2147 }
2148 
BodyOff(const ImportInfo & rInfo)2149 void ScHTMLTable::BodyOff( const ImportInfo& rInfo )
2150 {
2151     PushEntry( rInfo );
2152     if( !mpParentTable )
2153     {
2154         ImplDataOff();
2155         ImplRowOff();
2156     }
2157     CreateNewEntry( rInfo );
2158 }
2159 
CloseTable(const ImportInfo & rInfo)2160 ScHTMLTable* ScHTMLTable::CloseTable( const ImportInfo& rInfo )
2161 {
2162     if( mpParentTable )     // not allowed to close global table
2163     {
2164         PushEntry( rInfo, mbDataOn );
2165         ImplDataOff();
2166         ImplRowOff();
2167         mpParentTable->PushTableEntry( GetTableId() );
2168         mpParentTable->CreateNewEntry( rInfo );
2169         if( mbPreFormText ) // enclose preformatted table with empty lines in parent table
2170             mpParentTable->InsertLeadingEmptyLine();
2171         return mpParentTable;
2172     }
2173     return this;
2174 }
2175 
GetDocSize(ScHTMLOrient eOrient,SCCOLROW nCellPos) const2176 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
2177 {
2178     const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2179     size_t nIndex = static_cast< size_t >( nCellPos );
2180     if( nIndex >= rSizes.size() ) return 0;
2181     return (nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]);
2182 }
2183 
GetDocSize(ScHTMLOrient eOrient,SCCOLROW nCellBegin,SCCOLROW nCellEnd) const2184 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const
2185 {
2186     const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2187     size_t nBeginIdx = static_cast< size_t >( ::std::max< SCCOLROW >( nCellBegin, 0 ) );
2188     size_t nEndIdx = static_cast< size_t >( ::std::min< SCCOLROW >( nCellEnd, static_cast< SCCOLROW >( rSizes.size() ) ) );
2189     if (nBeginIdx >= nEndIdx ) return 0;
2190     return rSizes[ nEndIdx - 1 ] - ((nBeginIdx == 0) ? 0 : rSizes[ nBeginIdx - 1 ]);
2191 }
2192 
GetDocSize(ScHTMLOrient eOrient) const2193 SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient ) const
2194 {
2195     const ScSizeVec& rSizes = maCumSizes[ eOrient ];
2196     return rSizes.empty() ? 0 : rSizes.back();
2197 }
2198 
GetDocSize(const ScHTMLPos & rCellPos) const2199 ScHTMLSize ScHTMLTable::GetDocSize( const ScHTMLPos& rCellPos ) const
2200 {
2201     ScHTMLSize aCellSpan = GetSpan( rCellPos );
2202     return ScHTMLSize(
2203         static_cast< SCCOL >( GetDocSize( tdCol, rCellPos.mnCol, rCellPos.mnCol + aCellSpan.mnCols ) ),
2204         static_cast< SCROW >( GetDocSize( tdRow, rCellPos.mnRow, rCellPos.mnRow + aCellSpan.mnRows ) ) );
2205 }
2206 
GetDocPos(ScHTMLOrient eOrient,SCCOLROW nCellPos) const2207 SCCOLROW ScHTMLTable::GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
2208 {
2209     return maDocBasePos.Get( eOrient ) + GetDocSize( eOrient, 0, nCellPos );
2210 }
2211 
GetDocPos(const ScHTMLPos & rCellPos) const2212 ScHTMLPos ScHTMLTable::GetDocPos( const ScHTMLPos& rCellPos ) const
2213 {
2214     return ScHTMLPos(
2215         static_cast< SCCOL >( GetDocPos( tdCol, rCellPos.mnCol ) ),
2216         static_cast< SCROW >( GetDocPos( tdRow, rCellPos.mnRow ) ) );
2217 }
2218 
GetDocRange(ScRange & rRange) const2219 void ScHTMLTable::GetDocRange( ScRange& rRange ) const
2220 {
2221     rRange.aStart = rRange.aEnd = maDocBasePos.MakeAddr();
2222     rRange.aEnd.Move( static_cast< SCsCOL >( GetDocSize( tdCol ) ) - 1, static_cast< SCsROW >( GetDocSize( tdRow ) ) - 1, 0 );
2223 }
2224 
ApplyCellBorders(ScDocument * pDoc,const ScAddress & rFirstPos) const2225 void ScHTMLTable::ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const
2226 {
2227     DBG_ASSERT( pDoc, "ScHTMLTable::ApplyCellBorders - no document" );
2228     if( pDoc && mbBorderOn )
2229     {
2230         const SCCOL nLastCol = maSize.mnCols - 1;
2231         const SCROW nLastRow = maSize.mnRows - 1;
2232         const sal_uInt16 nOuterLine = DEF_LINE_WIDTH_2;
2233         const sal_uInt16 nInnerLine = DEF_LINE_WIDTH_0;
2234         SvxBorderLine aOuterLine, aInnerLine;
2235         aOuterLine.SetColor( Color( COL_BLACK ) );
2236         aOuterLine.SetOutWidth( nOuterLine );
2237         aInnerLine.SetColor( Color( COL_BLACK ) );
2238         aInnerLine.SetOutWidth( nInnerLine );
2239         SvxBoxItem aBorderItem( ATTR_BORDER );
2240 
2241         for( SCCOL nCol = 0; nCol <= nLastCol; ++nCol )
2242         {
2243             SvxBorderLine* pLeftLine = (nCol == 0) ? &aOuterLine : &aInnerLine;
2244             SvxBorderLine* pRightLine = (nCol == nLastCol) ? &aOuterLine : &aInnerLine;
2245             SCCOL nCellCol1 = static_cast< SCCOL >( GetDocPos( tdCol, nCol ) ) + rFirstPos.Col();
2246             SCCOL nCellCol2 = nCellCol1 + static_cast< SCCOL >( GetDocSize( tdCol, nCol ) ) - 1;
2247             for( SCROW nRow = 0; nRow <= nLastRow; ++nRow )
2248             {
2249                 SvxBorderLine* pTopLine = (nRow == 0) ? &aOuterLine : &aInnerLine;
2250                 SvxBorderLine* pBottomLine = (nRow == nLastRow) ? &aOuterLine : &aInnerLine;
2251                 SCROW nCellRow1 = GetDocPos( tdRow, nRow ) + rFirstPos.Row();
2252                 SCROW nCellRow2 = nCellRow1 + GetDocSize( tdRow, nRow ) - 1;
2253                 for( SCCOL nCellCol = nCellCol1; nCellCol <= nCellCol2; ++nCellCol )
2254                 {
2255                     aBorderItem.SetLine( (nCellCol == nCellCol1) ? pLeftLine : 0, BOX_LINE_LEFT );
2256                     aBorderItem.SetLine( (nCellCol == nCellCol2) ? pRightLine : 0, BOX_LINE_RIGHT );
2257                     for( SCROW nCellRow = nCellRow1; nCellRow <= nCellRow2; ++nCellRow )
2258                     {
2259                         aBorderItem.SetLine( (nCellRow == nCellRow1) ? pTopLine : 0, BOX_LINE_TOP );
2260                         aBorderItem.SetLine( (nCellRow == nCellRow2) ? pBottomLine : 0, BOX_LINE_BOTTOM );
2261                         pDoc->ApplyAttr( nCellCol, nCellRow, rFirstPos.Tab(), aBorderItem );
2262                     }
2263                 }
2264             }
2265         }
2266     }
2267 
2268     if ( mxNestedTables.get() )
2269     {
2270         for ( ScHTMLTableMap::const_iterator aIter( mxNestedTables->begin() );
2271              aIter != mxNestedTables->end(); ++aIter )
2272         {
2273             if ( aIter->second.get() )
2274                 aIter->second->ApplyCellBorders( pDoc, rFirstPos );
2275         }
2276     }
2277 }
2278 
2279 // ----------------------------------------------------------------------------
2280 
IsEmptyCell() const2281 bool ScHTMLTable::IsEmptyCell() const
2282 {
2283     return mpCurrEntryList && mpCurrEntryList->empty();
2284 }
2285 
IsSpaceCharInfo(const ImportInfo & rInfo)2286 bool ScHTMLTable::IsSpaceCharInfo( const ImportInfo& rInfo )
2287 {
2288     return (rInfo.nToken == HTML_TEXTTOKEN) && (rInfo.aText.Len() == 1) && (rInfo.aText.GetChar( 0 ) == ' ');
2289 }
2290 
CreateEntry() const2291 ScHTMLTable::ScHTMLEntryPtr ScHTMLTable::CreateEntry() const
2292 {
2293     return ScHTMLEntryPtr( new ScHTMLEntry( GetCurrItemSet() ) );
2294 }
2295 
CreateNewEntry(const ImportInfo & rInfo)2296 void ScHTMLTable::CreateNewEntry( const ImportInfo& rInfo )
2297 {
2298     DBG_ASSERT( !mxCurrEntry.get(), "ScHTMLTable::CreateNewEntry - old entry still present" );
2299     mxCurrEntry = CreateEntry();
2300     mxCurrEntry->aSel = rInfo.aSelection;
2301 }
2302 
ImplPushEntryToList(ScHTMLEntryList & rEntryList,ScHTMLEntryPtr & rxEntry)2303 void ScHTMLTable::ImplPushEntryToList( ScHTMLEntryList& rEntryList, ScHTMLEntryPtr& rxEntry )
2304 {
2305     // HTML entry list does not own the entries
2306     rEntryList.push_back( rxEntry.get() );
2307     // mrEEParseList (reference to member of ScEEParser) owns the entries
2308     mrEEParseList.Insert( rxEntry.release(), LIST_APPEND );
2309 }
2310 
PushEntry(ScHTMLEntryPtr & rxEntry)2311 bool ScHTMLTable::PushEntry( ScHTMLEntryPtr& rxEntry )
2312 {
2313     bool bPushed = false;
2314     if( rxEntry.get() && rxEntry->HasContents() )
2315     {
2316         if( mpCurrEntryList )
2317         {
2318             if( mbPushEmptyLine )
2319             {
2320                 ScHTMLEntryPtr xEmptyEntry = CreateEntry();
2321                 ImplPushEntryToList( *mpCurrEntryList, xEmptyEntry );
2322                 mbPushEmptyLine = false;
2323             }
2324             ImplPushEntryToList( *mpCurrEntryList, rxEntry );
2325             bPushed = true;
2326         }
2327         else if( mpParentTable )
2328         {
2329             bPushed = mpParentTable->PushEntry( rxEntry );
2330         }
2331         else
2332         {
2333             DBG_ERRORFILE( "ScHTMLTable::PushEntry - cannot push entry, no parent found" );
2334         }
2335     }
2336     return bPushed;
2337 }
2338 
PushEntry(const ImportInfo & rInfo,bool bLastInCell)2339 bool ScHTMLTable::PushEntry( const ImportInfo& rInfo, bool bLastInCell )
2340 {
2341     DBG_ASSERT( mxCurrEntry.get(), "ScHTMLTable::PushEntry - no current entry" );
2342     bool bPushed = false;
2343     if( mxCurrEntry.get() )
2344     {
2345         mxCurrEntry->AdjustEnd( rInfo );
2346         mxCurrEntry->Strip( mrEditEngine );
2347 
2348         // import entry always, if it is the last in cell, and cell is still empty
2349         if( bLastInCell && IsEmptyCell() )
2350         {
2351             mxCurrEntry->SetImportAlways();
2352             // don't insert empty lines before single empty entries
2353             if( mxCurrEntry->IsEmpty() )
2354                 mbPushEmptyLine = false;
2355         }
2356 
2357         bPushed = PushEntry( mxCurrEntry );
2358         mxCurrEntry.reset();
2359     }
2360     return bPushed;
2361 }
2362 
PushTableEntry(ScHTMLTableId nTableId)2363 bool ScHTMLTable::PushTableEntry( ScHTMLTableId nTableId )
2364 {
2365     DBG_ASSERT( nTableId != SC_HTML_GLOBAL_TABLE, "ScHTMLTable::PushTableEntry - cannot push global table" );
2366     bool bPushed = false;
2367     if( nTableId != SC_HTML_GLOBAL_TABLE )
2368     {
2369         ScHTMLEntryPtr xEntry( new ScHTMLEntry( maTableItemSet, nTableId ) );
2370         bPushed = PushEntry( xEntry );
2371     }
2372     return bPushed;
2373 }
2374 
GetExistingTable(ScHTMLTableId nTableId) const2375 ScHTMLTable* ScHTMLTable::GetExistingTable( ScHTMLTableId nTableId ) const
2376 {
2377     ScHTMLTable* pTable = ((nTableId != SC_HTML_GLOBAL_TABLE) && mxNestedTables.get()) ?
2378         mxNestedTables->FindTable( nTableId, false ) : 0;
2379     DBG_ASSERT( pTable || (nTableId == SC_HTML_GLOBAL_TABLE), "ScHTMLTable::GetExistingTable - table not found" );
2380     return pTable;
2381 }
2382 
InsertNestedTable(const ImportInfo & rInfo,bool bPreFormText)2383 ScHTMLTable* ScHTMLTable::InsertNestedTable( const ImportInfo& rInfo, bool bPreFormText )
2384 {
2385     if( !mxNestedTables.get() )
2386         mxNestedTables.reset( new ScHTMLTableMap( *this ) );
2387     if( bPreFormText )      // enclose new preformatted table with empty lines
2388         InsertLeadingEmptyLine();
2389     return mxNestedTables->CreateTable( rInfo, bPreFormText );
2390 }
2391 
InsertNewCell(const ScHTMLSize & rSpanSize)2392 void ScHTMLTable::InsertNewCell( const ScHTMLSize& rSpanSize )
2393 {
2394     ScRange* pRange;
2395 
2396     /*  Find an unused cell by skipping all merged ranges that cover the
2397         current cell position stored in maCurrCell. */
2398     while( ((pRange = maVMergedCells.Find( maCurrCell.MakeAddr() )) != 0) || ((pRange = maHMergedCells.Find( maCurrCell.MakeAddr() )) != 0) )
2399         maCurrCell.mnCol = pRange->aEnd.Col() + 1;
2400     mpCurrEntryList = &maEntryMap[ maCurrCell ];
2401 
2402     /*  If the new cell is merged horizontally, try to find collisions with
2403         other vertically merged ranges. In this case, shrink existing
2404         vertically merged ranges (do not shrink the new cell). */
2405     SCCOL nColEnd = maCurrCell.mnCol + rSpanSize.mnCols;
2406     for( ScAddress aAddr( maCurrCell.MakeAddr() ); aAddr.Col() < nColEnd; aAddr.IncCol() )
2407         if( (pRange = maVMergedCells.Find( aAddr )) != 0 )
2408             pRange->aEnd.SetRow( maCurrCell.mnRow - 1 );
2409 
2410     // insert the new range into the cell lists
2411     ScRange aNewRange( maCurrCell.MakeAddr() );
2412     aNewRange.aEnd.Move( rSpanSize.mnCols - 1, rSpanSize.mnRows - 1, 0 );
2413     if( rSpanSize.mnRows > 1 )
2414     {
2415         maVMergedCells.Append( aNewRange );
2416         /*  Do not insert vertically merged ranges into maUsedCells yet,
2417             because they may be shrunken (see above). The final vertically
2418             merged ranges are inserted in FillEmptyCells(). */
2419     }
2420     else
2421     {
2422         if( rSpanSize.mnCols > 1 )
2423             maHMergedCells.Append( aNewRange );
2424         /*  Insert horizontally merged ranges and single cells into
2425             maUsedCells, they will not be changed anymore. */
2426         maUsedCells.Join( aNewRange );
2427     }
2428 
2429     // adjust table size
2430     maSize.mnCols = ::std::max< SCCOL >( maSize.mnCols, aNewRange.aEnd.Col() + 1 );
2431     maSize.mnRows = ::std::max< SCROW >( maSize.mnRows, aNewRange.aEnd.Row() + 1 );
2432 }
2433 
ImplRowOn()2434 void ScHTMLTable::ImplRowOn()
2435 {
2436     if( mbRowOn )
2437         ImplRowOff();
2438     mxRowItemSet.reset( new SfxItemSet( maTableItemSet ) );
2439     maCurrCell.mnCol = 0;
2440     mbRowOn = true;
2441     mbDataOn = false;
2442 }
2443 
ImplRowOff()2444 void ScHTMLTable::ImplRowOff()
2445 {
2446     if( mbDataOn )
2447         ImplDataOff();
2448     if( mbRowOn )
2449     {
2450         mxRowItemSet.reset();
2451         ++maCurrCell.mnRow;
2452         mbRowOn = mbDataOn = false;
2453     }
2454 }
2455 
ImplDataOn(const ScHTMLSize & rSpanSize)2456 void ScHTMLTable::ImplDataOn( const ScHTMLSize& rSpanSize )
2457 {
2458     if( mbDataOn )
2459         ImplDataOff();
2460     if( !mbRowOn )
2461         ImplRowOn();
2462     mxDataItemSet.reset( new SfxItemSet( *mxRowItemSet ) );
2463     InsertNewCell( rSpanSize );
2464     mbDataOn = true;
2465     mbPushEmptyLine = false;
2466 }
2467 
ImplDataOff()2468 void ScHTMLTable::ImplDataOff()
2469 {
2470     if( mbDataOn )
2471     {
2472         mxDataItemSet.reset();
2473         ++maCurrCell.mnCol;
2474         mpCurrEntryList = 0;
2475         mbDataOn = false;
2476     }
2477 }
2478 
ProcessFormatOptions(SfxItemSet & rItemSet,const ImportInfo & rInfo)2479 void ScHTMLTable::ProcessFormatOptions( SfxItemSet& rItemSet, const ImportInfo& rInfo )
2480 {
2481     // special handling for table header cells
2482     if( rInfo.nToken == HTML_TABLEHEADER_ON )
2483     {
2484         rItemSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2485         rItemSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY ) );
2486     }
2487 
2488     for( ScHTMLOptionIterator aIter( rInfo ); aIter.is(); ++aIter )
2489     {
2490         switch( aIter->GetToken() )
2491         {
2492             case HTML_O_ALIGN:
2493             {
2494                 SvxCellHorJustify eVal = SVX_HOR_JUSTIFY_STANDARD;
2495                 const String& rOptVal = aIter->GetString();
2496                 if( rOptVal.EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_AL_right ) )
2497                     eVal = SVX_HOR_JUSTIFY_RIGHT;
2498                 else if( rOptVal.EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_AL_center ) )
2499                     eVal = SVX_HOR_JUSTIFY_CENTER;
2500                 else if( rOptVal.EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_AL_left ) )
2501                     eVal = SVX_HOR_JUSTIFY_LEFT;
2502                 if( eVal != SVX_HOR_JUSTIFY_STANDARD )
2503                     rItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY ) );
2504             }
2505             break;
2506 
2507             case HTML_O_VALIGN:
2508             {
2509                 SvxCellVerJustify eVal = SVX_VER_JUSTIFY_STANDARD;
2510                 const String& rOptVal = aIter->GetString();
2511                 if( rOptVal.EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_VA_top ) )
2512                     eVal = SVX_VER_JUSTIFY_TOP;
2513                 else if( rOptVal.EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
2514                     eVal = SVX_VER_JUSTIFY_CENTER;
2515                 else if( rOptVal.EqualsIgnoreCaseAscii( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
2516                     eVal = SVX_VER_JUSTIFY_BOTTOM;
2517                 if( eVal != SVX_VER_JUSTIFY_STANDARD )
2518                     rItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY ) );
2519             }
2520             break;
2521 
2522             case HTML_O_BGCOLOR:
2523             {
2524                 Color aColor;
2525                 aIter->GetColor( aColor );
2526                 rItemSet.Put( SvxBrushItem( aColor, ATTR_BACKGROUND ) );
2527             }
2528             break;
2529         }
2530     }
2531 }
2532 
SetDocSize(ScHTMLOrient eOrient,SCCOLROW nCellPos,SCCOLROW nSize)2533 void ScHTMLTable::SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize )
2534 {
2535     DBG_ASSERT( nCellPos >= 0, "ScHTMLTable::SetDocSize - unexpected negative position" );
2536     ScSizeVec& rSizes = maCumSizes[ eOrient ];
2537     size_t nIndex = static_cast< size_t >( nCellPos );
2538     // expand with height/width == 1
2539     while( nIndex >= rSizes.size() )
2540         rSizes.push_back( rSizes.empty() ? 1 : (rSizes.back() + 1) );
2541     // update size of passed position and all following
2542     // #i109987# only grow, don't shrink - use the largest needed size
2543     SCsCOLROW nDiff = nSize - ((nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]));
2544     if( nDiff > 0 )
2545         for( ScSizeVec::iterator aIt = rSizes.begin() + nIndex, aEnd = rSizes.end(); aIt != aEnd; ++aIt )
2546             *aIt += nDiff;
2547 }
2548 
CalcNeededDocSize(ScHTMLOrient eOrient,SCCOLROW nCellPos,SCCOLROW nCellSpan,SCCOLROW nRealDocSize)2549 void ScHTMLTable::CalcNeededDocSize(
2550         ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nCellSpan, SCCOLROW nRealDocSize )
2551 {
2552     SCCOLROW nDiffSize = 0;
2553     // in merged columns/rows: reduce needed size by size of leading columns
2554     while( nCellSpan > 1 )
2555     {
2556         nDiffSize += GetDocSize( eOrient, nCellPos );
2557         --nCellSpan;
2558         ++nCellPos;
2559     }
2560     // set remaining needed size to last column/row
2561     nRealDocSize -= ::std::min< SCCOLROW >( nRealDocSize - 1, nDiffSize );
2562     SetDocSize( eOrient, nCellPos, nRealDocSize );
2563 }
2564 
2565 // ----------------------------------------------------------------------------
2566 
FillEmptyCells()2567 void ScHTMLTable::FillEmptyCells()
2568 {
2569     if ( mxNestedTables.get() )
2570     {
2571         for ( ScHTMLTableMap::const_iterator aIter( mxNestedTables->begin() );
2572              aIter != mxNestedTables->end(); ++aIter )
2573         {
2574             if ( aIter->second.get() )
2575                 aIter->second->FillEmptyCells();
2576         }
2577     }
2578 
2579     // insert the final vertically merged ranges into maUsedCells
2580     for( const ScRange* pRange = maVMergedCells.First(); pRange; pRange = maVMergedCells.Next() )
2581         maUsedCells.Join( *pRange );
2582 
2583     for( ScAddress aAddr; aAddr.Row() < maSize.mnRows; aAddr.IncRow() )
2584     {
2585         for( aAddr.SetCol( 0 ); aAddr.Col() < maSize.mnCols; aAddr.IncCol() )
2586         {
2587             if( !maUsedCells.Find( aAddr ) )
2588             {
2589                 // create a range for the lock list (used to calc. cell span)
2590                 ScRange aRange( aAddr );
2591                 do
2592                 {
2593                     aRange.aEnd.IncCol();
2594                 }
2595                 while( (aRange.aEnd.Col() < maSize.mnCols) && !maUsedCells.Find( aRange.aEnd ) );
2596                 aRange.aEnd.IncCol( -1 );
2597                 maUsedCells.Join( aRange );
2598 
2599                 // insert a dummy entry
2600                 ScHTMLEntryPtr xEntry = CreateEntry();
2601                 ImplPushEntryToList( maEntryMap[ ScHTMLPos( aAddr ) ], xEntry );
2602             }
2603         }
2604     }
2605 }
2606 
RecalcDocSize()2607 void ScHTMLTable::RecalcDocSize()
2608 {
2609     // recalc table sizes recursively from inner to outer
2610     if ( mxNestedTables.get() )
2611     {
2612         for ( ScHTMLTableMap::const_iterator aIter( mxNestedTables->begin() );
2613              aIter != mxNestedTables->end(); ++aIter )
2614         {
2615             if ( aIter->second.get() )
2616                 aIter->second->RecalcDocSize();
2617         }
2618     }
2619 
2620     /*  Two passes: first calculates the sizes of single columns/rows, then
2621         the sizes of spanned columns/rows. This allows to fill nested tables
2622         into merged cells optimally. */
2623     static const sal_uInt16 PASS_SINGLE = 0;
2624     static const sal_uInt16 PASS_SPANNED = 1;
2625     for( sal_uInt16 nPass = PASS_SINGLE; nPass <= PASS_SPANNED; ++nPass )
2626     {
2627         // iterate through every table cell
2628         ScHTMLEntryMap::const_iterator aMapIterEnd = maEntryMap.end();
2629         for( ScHTMLEntryMap::const_iterator aMapIter = maEntryMap.begin(); aMapIter != aMapIterEnd; ++aMapIter )
2630         {
2631             const ScHTMLPos& rCellPos = aMapIter->first;
2632             ScHTMLSize aCellSpan = GetSpan( rCellPos );
2633 
2634             const ScHTMLEntryList& rEntryList = aMapIter->second;
2635             ScHTMLEntryList::const_iterator aListIter;
2636             ScHTMLEntryList::const_iterator aListIterEnd = rEntryList.end();
2637 
2638             // process the dimension of the current cell in this pass?
2639             // (pass is single and span is 1) or (pass is not single and span is not 1)
2640             bool bProcessColWidth = ((nPass == PASS_SINGLE) == (aCellSpan.mnCols == 1));
2641             bool bProcessRowHeight = ((nPass == PASS_SINGLE) == (aCellSpan.mnRows == 1));
2642             if( bProcessColWidth || bProcessRowHeight )
2643             {
2644                 ScHTMLSize aDocSize( 1, 0 );    // resulting size of the cell in document
2645 
2646                 // expand the cell size for each cell parse entry
2647                 for( aListIter = rEntryList.begin(); aListIter != aListIterEnd; ++aListIter )
2648                 {
2649                     ScHTMLTable* pTable = GetExistingTable( (*aListIter)->GetTableId() );
2650                     // find entry with maximum width
2651                     if( bProcessColWidth && pTable )
2652                         aDocSize.mnCols = ::std::max( aDocSize.mnCols, static_cast< SCCOL >( pTable->GetDocSize( tdCol ) ) );
2653                     // add up height of each entry
2654                     if( bProcessRowHeight )
2655                         aDocSize.mnRows += pTable ? pTable->GetDocSize( tdRow ) : 1;
2656                 }
2657                 if( !aDocSize.mnRows )
2658                     aDocSize.mnRows = 1;
2659 
2660                 if( bProcessColWidth )
2661                     CalcNeededDocSize( tdCol, rCellPos.mnCol, aCellSpan.mnCols, aDocSize.mnCols );
2662                 if( bProcessRowHeight )
2663                     CalcNeededDocSize( tdRow, rCellPos.mnRow, aCellSpan.mnRows, aDocSize.mnRows );
2664             }
2665         }
2666     }
2667 }
2668 
RecalcDocPos(const ScHTMLPos & rBasePos)2669 void ScHTMLTable::RecalcDocPos( const ScHTMLPos& rBasePos )
2670 {
2671     maDocBasePos = rBasePos;
2672     // after the previous assignment it is allowed to call GetDocPos() methods
2673 
2674     // iterate through every table cell
2675     ScHTMLEntryMap::iterator aMapIterEnd = maEntryMap.end();
2676     for( ScHTMLEntryMap::iterator aMapIter = maEntryMap.begin(); aMapIter != aMapIterEnd; ++aMapIter )
2677     {
2678         // fixed doc position of the entire cell (first entry)
2679         const ScHTMLPos aCellDocPos( GetDocPos( aMapIter->first ) );
2680         // fixed doc size of the entire cell
2681         const ScHTMLSize aCellDocSize( GetDocSize( aMapIter->first ) );
2682 
2683         // running doc position for single entries
2684         ScHTMLPos aEntryDocPos( aCellDocPos );
2685 
2686         ScHTMLEntryList& rEntryList = aMapIter->second;
2687         ScHTMLEntry* pEntry = 0;
2688         ScHTMLEntryList::iterator aListIterEnd = rEntryList.end();
2689         for( ScHTMLEntryList::iterator aListIter = rEntryList.begin(); aListIter != aListIterEnd; ++aListIter )
2690         {
2691             pEntry = *aListIter;
2692             if( ScHTMLTable* pTable = GetExistingTable( pEntry->GetTableId() ) )
2693             {
2694                 pTable->RecalcDocPos( aEntryDocPos );   // recalc nested table
2695                 pEntry->nCol = SCCOL_MAX;
2696                 pEntry->nRow = SCROW_MAX;
2697                 SCROW nTableRows = static_cast< SCROW >( pTable->GetDocSize( tdRow ) );
2698 
2699                 // use this entry to pad empty space right of table
2700                 if( mpParentTable )     // ... but not in global table
2701                 {
2702                     SCCOL nStartCol = aEntryDocPos.mnCol + static_cast< SCCOL >( pTable->GetDocSize( tdCol ) );
2703                     SCCOL nNextCol = aEntryDocPos.mnCol + aCellDocSize.mnCols;
2704                     if( nStartCol < nNextCol )
2705                     {
2706                         pEntry->nCol = nStartCol;
2707                         pEntry->nRow = aEntryDocPos.mnRow;
2708                         pEntry->nColOverlap = nNextCol - nStartCol;
2709                         pEntry->nRowOverlap = nTableRows;
2710                     }
2711                 }
2712                 aEntryDocPos.mnRow += nTableRows;
2713             }
2714             else
2715             {
2716                 pEntry->nCol = aEntryDocPos.mnCol;
2717                 pEntry->nRow = aEntryDocPos.mnRow;
2718                 if( mpParentTable )    // do not merge in global table
2719                     pEntry->nColOverlap = aCellDocSize.mnCols;
2720                 ++aEntryDocPos.mnRow;
2721             }
2722         }
2723 
2724         // pEntry points now to last entry.
2725         if( pEntry )
2726         {
2727             if( (pEntry == rEntryList.front()) && (pEntry->GetTableId() == SC_HTML_NO_TABLE) )
2728             {
2729                 // pEntry is the only entry in this cell - merge rows of cell with single non-table entry.
2730                 pEntry->nRowOverlap = aCellDocSize.mnRows;
2731             }
2732             else
2733             {
2734                 // #111667# fill up incomplete entry lists
2735                 SCROW nFirstUnusedRow = aCellDocPos.mnRow + aCellDocSize.mnRows;
2736                 while( aEntryDocPos.mnRow < nFirstUnusedRow )
2737                 {
2738                     ScHTMLEntryPtr xDummyEntry( new ScHTMLEntry( pEntry->GetItemSet() ) );
2739                     xDummyEntry->nCol = aEntryDocPos.mnCol;
2740                     xDummyEntry->nRow = aEntryDocPos.mnRow;
2741                     xDummyEntry->nColOverlap = aCellDocSize.mnCols;
2742                     ImplPushEntryToList( rEntryList, xDummyEntry );
2743                     ++aEntryDocPos.mnRow;
2744                 }
2745             }
2746         }
2747     }
2748 }
2749 
2750 // ============================================================================
2751 
ScHTMLGlobalTable(SfxItemPool & rPool,EditEngine & rEditEngine,ScEEParseList & rEEParseList,ScHTMLTableId & rnUnusedId)2752 ScHTMLGlobalTable::ScHTMLGlobalTable( SfxItemPool& rPool, EditEngine& rEditEngine, ScEEParseList& rEEParseList, ScHTMLTableId& rnUnusedId ) :
2753     ScHTMLTable( rPool, rEditEngine, rEEParseList, rnUnusedId )
2754 {
2755 }
2756 
~ScHTMLGlobalTable()2757 ScHTMLGlobalTable::~ScHTMLGlobalTable()
2758 {
2759 }
2760 
Recalc()2761 void ScHTMLGlobalTable::Recalc()
2762 {
2763     // Fills up empty cells with a dummy entry. */
2764     FillEmptyCells();
2765     // recalc table sizes of all nested tables and this table
2766     RecalcDocSize();
2767     // recalc document positions of all entries in this table and in nested tables
2768     RecalcDocPos( GetDocPos() );
2769 }
2770 
2771 // ============================================================================
2772 
ScHTMLQueryParser(EditEngine * pEditEngine,ScDocument * pDoc)2773 ScHTMLQueryParser::ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
2774     ScHTMLParser( pEditEngine, pDoc ),
2775     mnUnusedId( SC_HTML_GLOBAL_TABLE ),
2776     mbTitleOn( false )
2777 {
2778     mxGlobTable.reset( new ScHTMLGlobalTable( *pPool, *pEdit, *pList, mnUnusedId ) );
2779     mpCurrTable = mxGlobTable.get();
2780 }
2781 
~ScHTMLQueryParser()2782 ScHTMLQueryParser::~ScHTMLQueryParser()
2783 {
2784 }
2785 
Read(SvStream & rStrm,const String & rBaseURL)2786 sal_uLong ScHTMLQueryParser::Read( SvStream& rStrm, const String& rBaseURL  )
2787 {
2788     SvKeyValueIteratorRef xValues;
2789     SvKeyValueIterator* pAttributes = 0;
2790 
2791     SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
2792     if( pObjSh && pObjSh->IsLoading() )
2793     {
2794         pAttributes = pObjSh->GetHeaderAttributes();
2795     }
2796     else
2797     {
2798         /*  When not loading, set up fake HTTP headers to force the SfxHTMLParser
2799             to use UTF8 (used when pasting from clipboard) */
2800         const sal_Char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
2801         if( pCharSet )
2802         {
2803             String aContentType = String::CreateFromAscii( "text/html; charset=" );
2804             aContentType.AppendAscii( pCharSet );
2805 
2806             xValues = new SvKeyValueIterator;
2807             xValues->Append( SvKeyValue( String::CreateFromAscii( OOO_STRING_SVTOOLS_HTML_META_content_type ), aContentType ) );
2808             pAttributes = xValues;
2809         }
2810     }
2811 
2812     Link aOldLink = pEdit->GetImportHdl();
2813     pEdit->SetImportHdl( LINK( this, ScHTMLQueryParser, HTMLImportHdl ) );
2814     sal_uLong nErr = pEdit->Read( rStrm, rBaseURL, EE_FORMAT_HTML, pAttributes );
2815     pEdit->SetImportHdl( aOldLink );
2816 
2817     mxGlobTable->Recalc();
2818     nColMax = static_cast< SCCOL >( mxGlobTable->GetDocSize( tdCol ) - 1 );
2819     nRowMax = static_cast< SCROW >( mxGlobTable->GetDocSize( tdRow ) - 1 );
2820 
2821     return nErr;
2822 }
2823 
GetGlobalTable() const2824 const ScHTMLTable* ScHTMLQueryParser::GetGlobalTable() const
2825 {
2826     return mxGlobTable.get();
2827 }
2828 
ProcessToken(const ImportInfo & rInfo)2829 void ScHTMLQueryParser::ProcessToken( const ImportInfo& rInfo )
2830 {
2831     switch( rInfo.nToken )
2832     {
2833 // --- meta data ---
2834         case HTML_META:             MetaOn( rInfo );                break;  // <meta>
2835 
2836 // --- title handling ---
2837         case HTML_TITLE_ON:         TitleOn( rInfo );               break;  // <title>
2838         case HTML_TITLE_OFF:        TitleOff( rInfo );              break;  // </title>
2839 
2840 // --- body handling ---
2841         case HTML_BODY_ON:          mpCurrTable->BodyOn( rInfo );   break;  // <body>
2842         case HTML_BODY_OFF:         mpCurrTable->BodyOff( rInfo );  break;  // </body>
2843 
2844 // --- insert text ---
2845         case HTML_TEXTTOKEN:        InsertText( rInfo );            break;  // any text
2846         case HTML_LINEBREAK:        mpCurrTable->BreakOn();         break;  // <br>
2847         case HTML_HEAD1_ON:                                                 // <h1>
2848         case HTML_HEAD2_ON:                                                 // <h2>
2849         case HTML_HEAD3_ON:                                                 // <h3>
2850         case HTML_HEAD4_ON:                                                 // <h4>
2851         case HTML_HEAD5_ON:                                                 // <h5>
2852         case HTML_HEAD6_ON:                                                 // <h6>
2853         case HTML_PARABREAK_ON:     mpCurrTable->HeadingOn();       break;  // <p>
2854 
2855 // --- misc. contents ---
2856         case HTML_ANCHOR_ON:        mpCurrTable->AnchorOn();        break;  // <a>
2857 
2858 // --- table handling ---
2859         case HTML_TABLE_ON:         TableOn( rInfo );               break;  // <table>
2860         case HTML_TABLE_OFF:        TableOff( rInfo );              break;  // </table>
2861         case HTML_TABLEROW_ON:      mpCurrTable->RowOn( rInfo );    break;  // <tr>
2862         case HTML_TABLEROW_OFF:     mpCurrTable->RowOff( rInfo );   break;  // </tr>
2863         case HTML_TABLEHEADER_ON:                                           // <th>
2864         case HTML_TABLEDATA_ON:     mpCurrTable->DataOn( rInfo );   break;  // <td>
2865         case HTML_TABLEHEADER_OFF:                                          // </th>
2866         case HTML_TABLEDATA_OFF:    mpCurrTable->DataOff( rInfo );  break;  // </td>
2867         case HTML_PREFORMTXT_ON:    PreOn( rInfo );                 break;  // <pre>
2868         case HTML_PREFORMTXT_OFF:   PreOff( rInfo );                break;  // </pre>
2869 
2870 // --- formatting ---
2871         case HTML_FONT_ON:          FontOn( rInfo );                break;  // <font>
2872 
2873         case HTML_BIGPRINT_ON:      // <big>
2874             //! TODO: store current font size, use following size
2875             mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 3 ], 100, ATTR_FONT_HEIGHT ) );
2876         break;
2877         case HTML_SMALLPRINT_ON:    // <small>
2878             //! TODO: store current font size, use preceding size
2879             mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 0 ], 100, ATTR_FONT_HEIGHT ) );
2880         break;
2881 
2882         case HTML_BOLD_ON:          // <b>
2883         case HTML_STRONG_ON:        // <strong>
2884             mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2885         break;
2886 
2887         case HTML_ITALIC_ON:        // <i>
2888         case HTML_EMPHASIS_ON:      // <em>
2889         case HTML_ADDRESS_ON:       // <address>
2890         case HTML_BLOCKQUOTE_ON:    // <blockquote>
2891         case HTML_BLOCKQUOTE30_ON:  // <bq>
2892         case HTML_CITIATION_ON:     // <cite>
2893         case HTML_VARIABLE_ON:      // <var>
2894             mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
2895         break;
2896 
2897         case HTML_DEFINSTANCE_ON:   // <dfn>
2898             mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
2899             mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
2900         break;
2901 
2902         case HTML_UNDERLINE_ON:     // <u>
2903             mpCurrTable->PutItem( SvxUnderlineItem( UNDERLINE_SINGLE, ATTR_FONT_UNDERLINE ) );
2904         break;
2905     }
2906 }
2907 
InsertText(const ImportInfo & rInfo)2908 void ScHTMLQueryParser::InsertText( const ImportInfo& rInfo )
2909 {
2910     mpCurrTable->PutText( rInfo );
2911     if( mbTitleOn )
2912         maTitle.Append( rInfo.aText );
2913 }
2914 
FontOn(const ImportInfo & rInfo)2915 void ScHTMLQueryParser::FontOn( const ImportInfo& rInfo )
2916 {
2917     for( ScHTMLOptionIterator aIter( rInfo ); aIter.is(); ++aIter )
2918     {
2919         switch( aIter->GetToken() )
2920         {
2921             case HTML_O_FACE :
2922             {
2923                 const String& rFace = aIter->GetString();
2924                 String aFontName;
2925                 xub_StrLen nPos = 0;
2926                 while( nPos != STRING_NOTFOUND )
2927                 {
2928                     // font list separator: VCL = ';' HTML = ','
2929                     String aFName = rFace.GetToken( 0, ',', nPos );
2930                     aFName.EraseLeadingAndTrailingChars();
2931                     ScGlobal::AddToken( aFontName, aFName, ';' );
2932                 }
2933                 if ( aFontName.Len() )
2934                     mpCurrTable->PutItem( SvxFontItem( FAMILY_DONTKNOW,
2935                         aFontName, EMPTY_STRING, PITCH_DONTKNOW,
2936                         RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
2937             }
2938             break;
2939             case HTML_O_SIZE :
2940             {
2941                 sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( aIter->GetNumber(), 1, SC_HTML_FONTSIZES );
2942                 mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ nSize - 1 ], 100, ATTR_FONT_HEIGHT ) );
2943             }
2944             break;
2945             case HTML_O_COLOR :
2946             {
2947                 Color aColor;
2948                 aIter->GetColor( aColor );
2949                 mpCurrTable->PutItem( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
2950             }
2951             break;
2952         }
2953     }
2954 }
2955 
MetaOn(const ImportInfo & rInfo)2956 void ScHTMLQueryParser::MetaOn( const ImportInfo& rInfo )
2957 {
2958     if( mpDoc->GetDocumentShell() )
2959     {
2960         HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
2961 
2962         uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
2963             mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
2964         pParser->ParseMetaOptions(
2965             xDPS->getDocumentProperties(),
2966             mpDoc->GetDocumentShell()->GetHeaderAttributes() );
2967     }
2968 }
2969 
TitleOn(const ImportInfo &)2970 void ScHTMLQueryParser::TitleOn( const ImportInfo& /*rInfo*/ )
2971 {
2972     mbTitleOn = true;
2973     maTitle.Erase();
2974 }
2975 
TitleOff(const ImportInfo & rInfo)2976 void ScHTMLQueryParser::TitleOff( const ImportInfo& rInfo )
2977 {
2978     if( mbTitleOn )
2979     {
2980         maTitle.EraseLeadingAndTrailingChars();
2981         if( maTitle.Len() && mpDoc->GetDocumentShell() ) {
2982             uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
2983                 mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
2984 
2985             xDPS->getDocumentProperties()->setTitle(maTitle);
2986         }
2987         InsertText( rInfo );
2988         mbTitleOn = false;
2989     }
2990 }
2991 
TableOn(const ImportInfo & rInfo)2992 void ScHTMLQueryParser::TableOn( const ImportInfo& rInfo )
2993 {
2994     mpCurrTable = mpCurrTable->TableOn( rInfo );
2995 }
2996 
TableOff(const ImportInfo & rInfo)2997 void ScHTMLQueryParser::TableOff( const ImportInfo& rInfo )
2998 {
2999     mpCurrTable = mpCurrTable->TableOff( rInfo );
3000 }
3001 
PreOn(const ImportInfo & rInfo)3002 void ScHTMLQueryParser::PreOn( const ImportInfo& rInfo )
3003 {
3004     mpCurrTable = mpCurrTable->PreOn( rInfo );
3005 }
3006 
PreOff(const ImportInfo & rInfo)3007 void ScHTMLQueryParser::PreOff( const ImportInfo& rInfo )
3008 {
3009     mpCurrTable = mpCurrTable->PreOff( rInfo );
3010 }
3011 
CloseTable(const ImportInfo & rInfo)3012 void ScHTMLQueryParser::CloseTable( const ImportInfo& rInfo )
3013 {
3014     mpCurrTable = mpCurrTable->CloseTable( rInfo );
3015 }
3016 
3017 // ----------------------------------------------------------------------------
3018 
IMPL_LINK(ScHTMLQueryParser,HTMLImportHdl,const ImportInfo *,pInfo)3019 IMPL_LINK( ScHTMLQueryParser, HTMLImportHdl, const ImportInfo*, pInfo )
3020 {
3021     switch( pInfo->eState )
3022     {
3023         case HTMLIMP_START:
3024         break;
3025 
3026         case HTMLIMP_NEXTTOKEN:
3027         case HTMLIMP_UNKNOWNATTR:
3028             ProcessToken( *pInfo );
3029         break;
3030 
3031         case HTMLIMP_INSERTPARA:
3032             mpCurrTable->InsertPara( *pInfo );
3033         break;
3034 
3035         case HTMLIMP_SETATTR:
3036         case HTMLIMP_INSERTTEXT:
3037         case HTMLIMP_INSERTFIELD:
3038         break;
3039 
3040         case HTMLIMP_END:
3041             while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
3042                 CloseTable( *pInfo );
3043         break;
3044 
3045         default:
3046             DBG_ERRORFILE( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
3047     }
3048     return 0;
3049 }
3050 
3051 // ============================================================================
3052 
3053