fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / filter / excel / xehelper.cxx
blob0109d5eef95c54de4ec9f21b4a8e7d8673cd3708
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/i18n/XBreakIterator.hpp>
21 #include <com/sun/star/i18n/ScriptType.hpp>
22 #include <sfx2/objsh.hxx>
23 #include <vcl/font.hxx>
24 #include <tools/urlobj.hxx>
25 #include <svl/itemset.hxx>
26 #include <svtools/ctrltool.hxx>
27 #include <svx/svdotext.hxx>
28 #include <editeng/outlobj.hxx>
29 #include "scitems.hxx"
30 #include <editeng/fhgtitem.hxx>
31 #include <editeng/flstitem.hxx>
32 #include <editeng/colritem.hxx>
33 #include <editeng/eeitem.hxx>
34 #include <editeng/flditem.hxx>
35 #include <editeng/escapementitem.hxx>
36 #include <editeng/svxfont.hxx>
38 #include "document.hxx"
39 #include "docpool.hxx"
40 #include "formulacell.hxx"
41 #include "editutil.hxx"
42 #include "patattr.hxx"
43 #include "scmatrix.hxx"
44 #include "xestyle.hxx"
45 #include "fprogressbar.hxx"
46 #include "xltracer.hxx"
47 #include "xecontent.hxx"
48 #include "xelink.hxx"
49 #include "xehelper.hxx"
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::i18n::XBreakIterator;
54 // Export progress bar ========================================================
56 XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
57 XclExpRoot( rRoot ),
58 mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
59 mpSubProgress( 0 ),
60 mpSubRowCreate( 0 ),
61 mpSubRowFinal( 0 ),
62 mnSegRowFinal( SCF_INV_SEGMENT ),
63 mnRowCount( 0 )
67 XclExpProgressBar::~XclExpProgressBar()
71 void XclExpProgressBar::Initialize()
73 const ScDocument& rDoc = GetDoc();
74 const XclExpTabInfo& rTabInfo = GetTabInfo();
75 SCTAB nScTabCount = rTabInfo.GetScTabCount();
77 // *** segment: creation of ROW records *** -------------------------------
79 sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
80 mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
81 maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
83 for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
85 if( rTabInfo.IsExportTab( nScTab ) )
87 SCCOL nLastUsedScCol;
88 SCROW nLastUsedScRow;
89 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
90 sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 );
91 maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
95 // *** segment: writing all ROW records *** -------------------------------
97 mnSegRowFinal = mxProgress->AddSegment( 1000 );
98 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
101 void XclExpProgressBar::IncRowRecordCount()
103 ++mnRowCount;
106 void XclExpProgressBar::ActivateCreateRowsSegment()
108 OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
109 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
110 sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
111 OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
112 if( nSeg != SCF_INV_SEGMENT )
114 mpSubProgress = mpSubRowCreate;
115 mpSubProgress->ActivateSegment( nSeg );
117 else
118 mpSubProgress = 0;
121 void XclExpProgressBar::ActivateFinalRowsSegment()
123 if( !mpSubRowFinal && (mnRowCount > 0) )
125 mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
126 mpSubRowFinal->AddSegment( mnRowCount );
128 mpSubProgress = mpSubRowFinal;
129 if( mpSubProgress )
130 mpSubProgress->Activate();
133 void XclExpProgressBar::Progress()
135 if( mpSubProgress && !mpSubProgress->IsFull() )
136 mpSubProgress->Progress();
139 // Calc->Excel cell address/range conversion ==================================
141 namespace {
143 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
144 inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
146 rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
147 rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
150 } // namespace
152 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
153 XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
157 // cell address ---------------------------------------------------------------
159 bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
161 // ScAddress::operator<=() doesn't do what we want here
162 bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
163 bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
164 bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
166 bool bValid = bValidCol && bValidRow && bValidTab;
167 if( !bValid )
169 mbColTrunc |= !bValidCol;
170 mbRowTrunc |= !bValidRow;
172 if( !bValid && bWarn )
174 mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
175 mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
177 return bValid;
180 bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
181 const ScAddress& rScPos, bool bWarn )
183 bool bValid = CheckAddress( rScPos, bWarn );
184 if( bValid )
185 lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
186 return bValid;
189 XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
191 XclAddress aXclPos( ScAddress::UNINITIALIZED );
192 if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
193 lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
194 return aXclPos;
197 // cell range -----------------------------------------------------------------
199 bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
201 return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
204 bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
206 rScRange.Justify();
208 // check start position
209 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
210 if( bValidStart )
212 // check & correct end position
213 ScAddress& rScEnd = rScRange.aEnd;
214 if( !CheckAddress( rScEnd, bWarn ) )
216 rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
217 rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
218 rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
222 return bValidStart;
225 bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
226 const ScRange& rScRange, bool bWarn )
228 // check start position
229 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
230 if( bValidStart )
232 lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
234 // check & correct end position
235 SCCOL nScCol2 = rScRange.aEnd.Col();
236 SCROW nScRow2 = rScRange.aEnd.Row();
237 if( !CheckAddress( rScRange.aEnd, bWarn ) )
239 nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
240 nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
242 lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
244 return bValidStart;
247 // cell range list ------------------------------------------------------------
249 void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
251 for ( size_t nRange = rScRanges.size(); nRange > 0; )
253 ScRange* pScRange = rScRanges[ --nRange ];
254 if( !CheckRange( *pScRange, bWarn ) )
255 delete rScRanges.Remove(nRange);
259 void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
260 const ScRangeList& rScRanges, bool bWarn )
262 rXclRanges.clear();
263 for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
265 if( const ScRange* pScRange = rScRanges[ nPos ] )
267 XclRange aXclRange( ScAddress::UNINITIALIZED );
268 if( ConvertRange( aXclRange, *pScRange, bWarn ) )
269 rXclRanges.push_back( aXclRange );
274 // EditEngine->String conversion ==============================================
276 namespace {
278 OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
280 const OUString& aRepr = rUrlField.GetRepresentation();
281 // no representation -> use URL
282 return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
285 } // namespace
287 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
288 XclExpRoot( rRoot ),
289 maScPos( rScPos ),
290 mbMultipleUrls( false )
294 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
298 OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
300 OUString aUrlRepr;
302 if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
304 // there was/is already a HLINK record
305 mbMultipleUrls = static_cast< bool >(mxLinkRec);
307 mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) );
309 if( const OUString* pRepr = mxLinkRec->GetRepr() )
310 aUrlRepr = *pRepr;
312 // add URL to note text
313 maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
316 // no hyperlink representation from Excel HLINK record -> use it from text field
317 return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
320 bool XclExpHyperlinkHelper::HasLinkRecord() const
322 return !mbMultipleUrls && mxLinkRec;
325 XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord()
327 if( HasLinkRecord() )
328 return mxLinkRec;
329 return XclExpHyperlinkRef();
332 namespace {
334 /** Creates a new formatted string from the passed unformatted string.
336 Creates a Unicode string or a byte string, depending on the current BIFF
337 version contained in the passed XclExpRoot object. May create a formatted
338 string object, if the text contains different script types.
340 @param pCellAttr
341 Cell attributes used for font formatting.
342 @param nFlags
343 Modifiers for string export.
344 @param nMaxLen
345 The maximum number of characters to store in this string.
346 @return
347 The new string object.
349 XclExpStringRef lclCreateFormattedString(
350 const XclExpRoot& rRoot, const OUString& rText, const ScPatternAttr* pCellAttr,
351 XclStrFlags nFlags, sal_uInt16 nMaxLen )
353 /* Create an empty Excel string object with correctly initialized BIFF mode,
354 because this function only uses Append() functions that require this. */
355 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
357 // script type handling
358 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
359 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
360 // #i63255# get script type for leading weak characters
361 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
363 // font buffer and cell item set
364 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
365 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
367 // process all script portions
368 OUString aOUText( rText );
369 sal_Int32 nPortionPos = 0;
370 sal_Int32 nTextLen = aOUText.getLength();
371 while( nPortionPos < nTextLen )
373 // get script type and end position of next script portion
374 sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos );
375 sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript );
377 // reuse previous script for following weak portions
378 if( nScript == ApiScriptType::WEAK )
379 nScript = nLastScript;
381 // construct font from current text portion
382 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) );
384 // Excel start position of this portion
385 sal_Int32 nXclPortionStart = xString->Len();
386 // add portion text to Excel string
387 XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
388 if( nXclPortionStart < xString->Len() )
390 // insert font into buffer
391 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
392 // insert font index into format run vector
393 xString->AppendFormat( nXclPortionStart, nFontIdx );
396 // go to next script portion
397 nLastScript = nScript;
398 nPortionPos = nPortionEnd;
401 return xString;
404 /** Creates a new formatted string from an edit engine text object.
406 Creates a Unicode string or a byte string, depending on the current BIFF
407 version contained in the passed XclExpRoot object.
409 @param rEE
410 The edit engine in use. The text object must already be set.
411 @param nFlags
412 Modifiers for string export.
413 @param nMaxLen
414 The maximum number of characters to store in this string.
415 @return
416 The new string object.
418 XclExpStringRef lclCreateFormattedString(
419 const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
420 XclStrFlags nFlags, sal_uInt16 nMaxLen )
422 /* Create an empty Excel string object with correctly initialized BIFF mode,
423 because this function only uses Append() functions that require this. */
424 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
426 // font buffer and helper item set for edit engine -> Calc item conversion
427 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
428 SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
430 // script type handling
431 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
432 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
433 // #i63255# get script type for leading weak characters
434 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
436 // process all paragraphs
437 sal_Int32 nParaCount = rEE.GetParagraphCount();
438 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
440 ESelection aSel( nPara, 0 );
441 OUString aParaText( rEE.GetText( nPara ) );
443 std::vector<sal_Int32> aPosList;
444 rEE.GetPortions( nPara, aPosList );
446 // process all portions in the paragraph
447 for( std::vector<sal_Int32>::const_iterator it(aPosList.begin()); it != aPosList.end(); ++it )
449 aSel.nEndPos = *it;
450 OUString aXclPortionText = aParaText.copy( aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
452 aItemSet.ClearItem();
453 SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
454 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
456 // get escapement value
457 short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc();
459 // process text fields
460 bool bIsHyperlink = false;
461 if( aSel.nStartPos + 1 == aSel.nEndPos )
463 // test if the character is a text field
464 const SfxPoolItem* pItem;
465 if( aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SfxItemState::SET )
467 const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField();
468 if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) )
470 // convert URL field to string representation
471 aXclPortionText = pLinkHelper ?
472 pLinkHelper->ProcessUrlField( *pUrlField ) :
473 lclGetUrlRepresentation( *pUrlField );
474 bIsHyperlink = true;
476 else
478 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
479 aXclPortionText.clear();
484 // Excel start position of this portion
485 sal_Int32 nXclPortionStart = xString->Len();
486 // add portion text to Excel string
487 XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
488 if( (nXclPortionStart < xString->Len()) || (aParaText.isEmpty()) )
490 /* Construct font from current edit engine text portion. Edit engine
491 creates different portions for different script types, no need to loop. */
492 sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
493 if( nScript == ApiScriptType::WEAK )
494 nScript = nLastScript;
495 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) );
496 nLastScript = nScript;
498 // add escapement
499 aFont.SetEscapement( nEsc );
500 // modify automatic font color for hyperlinks
501 if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) )
502 aFont.SetColor( Color( COL_LIGHTBLUE ) );
504 // insert font into buffer
505 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
506 // insert font index into format run vector
507 xString->AppendFormat( nXclPortionStart, nFontIdx );
510 aSel.nStartPos = aSel.nEndPos;
513 // add trailing newline (important for correct character index calculation)
514 if( nPara + 1 < nParaCount )
515 XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
518 return xString;
521 } // namespace
523 XclExpStringRef XclExpStringHelper::CreateString(
524 const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
526 XclExpStringRef xString( new XclExpString );
527 if( rRoot.GetBiff() == EXC_BIFF8 )
528 xString->Assign( rString, nFlags, nMaxLen );
529 else
530 xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
531 return xString;
534 XclExpStringRef XclExpStringHelper::CreateString(
535 const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
537 XclExpStringRef xString = CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
538 AppendChar( *xString, rRoot, cChar );
539 return xString;
542 void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const OUString& rString )
544 if( rRoot.GetBiff() == EXC_BIFF8 )
545 rXclString.Append( rString );
546 else
547 rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
550 void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
552 if( rRoot.GetBiff() == EXC_BIFF8 )
553 rXclString.Append( OUString(cChar) );
554 else
555 rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
558 XclExpStringRef XclExpStringHelper::CreateCellString(
559 const XclExpRoot& rRoot, const OUString& rString, const ScPatternAttr* pCellAttr,
560 XclStrFlags nFlags, sal_uInt16 nMaxLen )
562 return lclCreateFormattedString(rRoot, rString, pCellAttr, nFlags, nMaxLen);
565 XclExpStringRef XclExpStringHelper::CreateCellString(
566 const XclExpRoot& rRoot, const EditTextObject& rEditText, const ScPatternAttr* pCellAttr,
567 XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
569 XclExpStringRef xString;
571 // formatted cell
572 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
573 bool bOldUpdateMode = rEE.GetUpdateMode();
574 rEE.SetUpdateMode( true );
576 // default items
577 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
578 SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() );
579 ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
580 rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership
582 // create the string
583 rEE.SetText(rEditText);
584 xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
585 rEE.SetUpdateMode( bOldUpdateMode );
587 return xString;
590 XclExpStringRef XclExpStringHelper::CreateString(
591 const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
592 XclStrFlags nFlags, sal_uInt16 nMaxLen )
594 XclExpStringRef xString;
595 if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
597 EditEngine& rEE = rRoot.GetDrawEditEngine();
598 bool bOldUpdateMode = rEE.GetUpdateMode();
599 rEE.SetUpdateMode( true );
600 // create the string
601 rEE.SetText( pParaObj->GetTextObject() );
602 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
603 rEE.SetUpdateMode( bOldUpdateMode );
604 // limit formats - TODO: BIFF dependent
605 if( !xString->IsEmpty() )
607 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
608 xString->AppendTrailingFormat( EXC_FONT_APP );
611 else
613 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
614 // create BIFF dependent empty Excel string
615 xString = CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
617 return xString;
620 XclExpStringRef XclExpStringHelper::CreateString(
621 const XclExpRoot& rRoot, const EditTextObject& rEditObj,
622 XclStrFlags nFlags, sal_uInt16 nMaxLen )
624 XclExpStringRef xString;
625 EditEngine& rEE = rRoot.GetDrawEditEngine();
626 bool bOldUpdateMode = rEE.GetUpdateMode();
627 rEE.SetUpdateMode( true );
628 rEE.SetText( rEditObj );
629 xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen );
630 rEE.SetUpdateMode( bOldUpdateMode );
631 // limit formats - TODO: BIFF dependent
632 if( !xString->IsEmpty() )
634 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
635 xString->AppendTrailingFormat( EXC_FONT_APP );
637 return xString;
640 sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString )
642 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
643 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
644 OUString aOUString( rString );
645 sal_Int32 nStrPos = 0;
646 sal_Int32 nStrLen = aOUString.getLength();
647 sal_Int16 nScript = ApiScriptType::WEAK;
648 while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
650 nScript = xBreakIt->getScriptType( aOUString, nStrPos );
651 nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript );
653 return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
656 // Header/footer conversion ===================================================
658 XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
659 XclExpRoot( rRoot ),
660 mrEE( rRoot.GetHFEditEngine() ),
661 mnTotalHeight( 0 )
665 void XclExpHFConverter::GenerateString(
666 const EditTextObject* pLeftObj,
667 const EditTextObject* pCenterObj,
668 const EditTextObject* pRightObj )
670 maHFString.clear();
671 mnTotalHeight = 0;
672 AppendPortion( pLeftObj, 'L' );
673 AppendPortion( pCenterObj, 'C' );
674 AppendPortion( pRightObj, 'R' );
677 void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
679 if( !pTextObj ) return;
681 OUString aText;
682 sal_Int32 nHeight = 0;
683 SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END );
685 // edit engine
686 bool bOldUpdateMode = mrEE.GetUpdateMode();
687 mrEE.SetUpdateMode( true );
688 mrEE.SetText( *pTextObj );
690 // font information
691 XclFontData aFontData, aNewData;
692 if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
694 aFontData = pFirstFont->GetFontData();
695 (aFontData.mnHeight += 10) /= 20; // using pt here, not twips
697 else
698 aFontData.mnHeight = 10;
700 const FontList* pFontList = 0;
701 if( SfxObjectShell* pDocShell = GetDocShell() )
703 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
704 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
705 pFontList = pInfoItem->GetFontList();
708 sal_Int32 nParaCount = mrEE.GetParagraphCount();
709 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
711 ESelection aSel( nPara, 0 );
712 OUString aParaText;
713 sal_Int32 nParaHeight = 0;
714 std::vector<sal_Int32> aPosList;
715 mrEE.GetPortions( nPara, aPosList );
717 for( std::vector<sal_Int32>::const_iterator it( aPosList.begin() ); it != aPosList.end(); ++it )
719 aSel.nEndPos = *it;
720 if( aSel.nStartPos < aSel.nEndPos )
723 // --- font attributes ---
725 vcl::Font aFont;
726 aItemSet.ClearItem();
727 SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
728 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
729 ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
731 // font name and style
732 aNewData.maName = XclTools::GetXclFontName( aFont.GetName() );
733 aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
734 aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
735 bool bNewFont = !(aFontData.maName == aNewData.maName);
736 bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
737 (aFontData.mbItalic != aNewData.mbItalic);
738 if( bNewFont || (bNewStyle && pFontList) )
740 aParaText = "&\"" + OUString(aNewData.maName);
741 if( pFontList )
743 vcl::FontInfo aFontInfo( pFontList->Get(
744 aNewData.maName,
745 (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
746 aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
747 aNewData.maStyle = pFontList->GetStyleName( aFontInfo );
748 if( !aNewData.maStyle.isEmpty() )
749 aParaText += "," + aNewData.maStyle;
751 aParaText += "\"";
754 // height
755 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
756 // -> get it directly from edit engine item set
757 aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() );
758 (aNewData.mnHeight += 10) /= 20;
759 bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
760 if( bFontHtChanged )
761 aParaText += "&" + OUString::number( aNewData.mnHeight );
762 // update maximum paragraph height, convert to twips
763 nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
765 // underline
766 aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
767 switch( aFont.GetUnderline() )
769 case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
770 case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
771 case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
772 default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
774 if( aFontData.mnUnderline != aNewData.mnUnderline )
776 sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
777 aFontData.mnUnderline : aNewData.mnUnderline;
778 (nTmpUnderl == EXC_FONTUNDERL_SINGLE)? aParaText += "&U" : aParaText += "&E";
781 // strikeout
782 aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
783 if( aFontData.mbStrikeout != aNewData.mbStrikeout )
784 aParaText += "&S";
786 // super/sub script
787 const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT );
788 aNewData.SetScEscapement( rEscapeItem.GetEsc() );
789 if( aFontData.mnEscapem != aNewData.mnEscapem )
791 switch(aNewData.mnEscapem)
793 // close the previous super/sub script.
794 case EXC_FONTESC_NONE: (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? aParaText += "&X" : aParaText += "&Y"; break;
795 case EXC_FONTESC_SUPER: aParaText += "&X"; break;
796 case EXC_FONTESC_SUB: aParaText += "&Y"; break;
797 default: break;
801 aFontData = aNewData;
803 // --- text content or text fields ---
805 const SfxPoolItem* pItem;
806 if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
807 (aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SfxItemState::SET) )
809 if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() )
811 if( pFieldData->ISA( SvxPageField ) )
812 aParaText += "&P";
813 else if( pFieldData->ISA( SvxPagesField ) )
814 aParaText += "&N";
815 else if( pFieldData->ISA( SvxDateField ) )
816 aParaText += "&D";
817 else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) )
818 aParaText += "&T";
819 else if( pFieldData->ISA( SvxTableField ) )
820 aParaText += "&A";
821 else if( pFieldData->ISA( SvxFileField ) ) // title -> file name
822 aParaText += "&F";
823 else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) )
825 switch( pFileField->GetFormat() )
827 case SVXFILEFORMAT_NAME_EXT:
828 case SVXFILEFORMAT_NAME:
829 aParaText += "&F";
830 break;
831 case SVXFILEFORMAT_PATH:
832 aParaText += "&Z";
833 break;
834 case SVXFILEFORMAT_FULLPATH:
835 aParaText += "&Z&F";
836 break;
837 default:
838 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
843 else
845 OUString aPortionText( mrEE.GetText( aSel ) );
846 aPortionText = aPortionText.replaceAll( "&", "&&" );
847 // #i17440# space between font height and numbers in text
848 if( bFontHtChanged && aParaText.getLength() && !aPortionText.isEmpty() )
850 sal_Unicode cLast = aParaText[ aParaText.getLength() - 1 ];
851 sal_Unicode cFirst = aPortionText[0];
852 if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
853 aParaText += " ";
855 aParaText += aPortionText;
859 aSel.nStartPos = aSel.nEndPos;
862 aText = ScGlobal::addToken( aText, aParaText, '\n' );
863 if( nParaHeight == 0 )
864 nParaHeight = aFontData.mnHeight * 20; // points -> twips
865 nHeight += nParaHeight;
868 mrEE.SetUpdateMode( bOldUpdateMode );
870 if( !aText.isEmpty() )
872 maHFString += "&" + OUString(cPortionCode) + aText;
873 mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
877 // URL conversion =============================================================
879 namespace {
881 /** Encodes special parts of the URL, i.e. directory separators and volume names.
882 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
883 OUString lclEncodeDosUrl(
884 XclBiff eBiff, const OUString& rUrl, const OUString& rBase, const OUString* pTableName)
886 OUStringBuffer aBuf;
888 if (!rUrl.isEmpty())
890 OUString aOldUrl = rUrl;
891 aBuf.append(EXC_URLSTART_ENCODED);
893 if ( aOldUrl.getLength() > 2 && aOldUrl.copy(0,2) == "\\\\" )
895 // UNC
896 aBuf.append(EXC_URL_DOSDRIVE).append('@');
897 aOldUrl = aOldUrl.copy(2);
899 else if ( aOldUrl.getLength() > 2 && aOldUrl.copy(1,2) == ":\\" )
901 // drive letter
902 sal_Unicode cThisDrive = rBase.isEmpty() ? ' ' : rBase[0];
903 sal_Unicode cDrive = aOldUrl[0];
904 if (cThisDrive == cDrive)
905 // This document and the referenced document are under the same drive.
906 aBuf.append(EXC_URL_DRIVEROOT);
907 else
908 aBuf.append(EXC_URL_DOSDRIVE).append(cDrive);
909 aOldUrl = aOldUrl.copy(3);
911 else
913 // URL probably points to a document on a Unix-like file system
914 aBuf.append(EXC_URL_DRIVEROOT);
917 // directories
918 sal_Int32 nPos = -1;
919 while((nPos = aOldUrl.indexOf('\\')) != -1)
921 if ( aOldUrl.copy(0,2) == ".." )
922 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
923 // Excel seems confused by this token).
924 aBuf.append(EXC_URL_PARENTDIR);
925 else
926 aBuf.append(aOldUrl.copy(0,nPos)).append(EXC_URL_SUBDIR);
928 aOldUrl = aOldUrl.copy(nPos + 1);
931 // file name
932 if (pTableName) // enclose file name in brackets if table name follows
933 aBuf.append('[').append(aOldUrl).append(']');
934 else
935 aBuf.append(aOldUrl);
937 else // empty URL -> self reference
939 switch( eBiff )
941 case EXC_BIFF5:
942 aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
943 break;
944 case EXC_BIFF8:
945 DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
946 aBuf.append(EXC_URLSTART_SELF);
947 break;
948 default:
949 DBG_ERROR_BIFF();
953 // table name
954 if (pTableName)
955 aBuf.append(*pTableName);
957 // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
958 // It's better to truncate, than generate invalid file that Excel cannot open.
959 if (aBuf.getLength() > 255)
960 aBuf.setLength(255);
962 return aBuf.makeStringAndClear();
965 } // namespace
967 OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const OUString& rAbsUrl, const OUString* pTableName )
969 OUString aDosUrl = INetURLObject(rAbsUrl).getFSysPath(INetURLObject::FSYS_DOS);
970 OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(INetURLObject::FSYS_DOS);
971 return lclEncodeDosUrl(rRoot.GetBiff(), aDosUrl, aDosBase, pTableName);
974 OUString XclExpUrlHelper::EncodeDde( const OUString& rApplic, const OUString& rTopic )
976 OUStringBuffer aBuf;
977 aBuf.append(rApplic).append(EXC_DDE_DELIM).append(rTopic);
978 return aBuf.makeStringAndClear();
981 // Cached Value Lists =========================================================
983 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
984 : mrMatrix( rMatrix )
986 mrMatrix.IncRef();
988 XclExpCachedMatrix::~XclExpCachedMatrix()
990 mrMatrix.DecRef();
993 void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
995 mrMatrix.GetDimensions( nCols, nRows );
997 OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
998 OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1001 sal_Size XclExpCachedMatrix::GetSize() const
1003 SCSIZE nCols, nRows;
1005 GetDimensions( nCols, nRows );
1007 /* The returned size may be wrong if the matrix contains strings. The only
1008 effect is that the export stream has to update a wrong record size which is
1009 faster than to iterate through all cached values and calculate their sizes. */
1010 return 3 + 9 * (nCols * nRows);
1013 void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
1015 SCSIZE nCols, nRows;
1017 GetDimensions( nCols, nRows );
1019 if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
1020 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1021 rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
1022 else
1023 // in BIFF8: columns and rows decreaed by 1
1024 rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
1026 for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
1028 for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
1030 ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
1032 if( SC_MATVAL_EMPTY == nMatVal.nType )
1034 rStrm.SetSliceSize( 9 );
1035 rStrm << EXC_CACHEDVAL_EMPTY;
1036 rStrm.WriteZeroBytes( 8 );
1038 else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
1040 XclExpString aStr( nMatVal.GetString().getString(), EXC_STR_DEFAULT );
1041 rStrm.SetSliceSize( 6 );
1042 rStrm << EXC_CACHEDVAL_STRING << aStr;
1044 else if( SC_MATVAL_BOOLEAN == nMatVal.nType )
1046 sal_Int8 nBool = sal_Int8(nMatVal.GetBoolean());
1047 rStrm.SetSliceSize( 9 );
1048 rStrm << EXC_CACHEDVAL_BOOL << nBool;
1049 rStrm.WriteZeroBytes( 7 );
1051 else if( sal_uInt16 nScError = nMatVal.GetError() )
1053 sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
1054 rStrm.SetSliceSize( 9 );
1055 rStrm << EXC_CACHEDVAL_ERROR << nError;
1056 rStrm.WriteZeroBytes( 7 );
1058 else
1060 rStrm.SetSliceSize( 9 );
1061 rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
1067 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */