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