1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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/escpitem.hxx>
36 #include <editeng/svxfont.hxx>
38 #include "document.hxx"
39 #include "docpool.hxx"
41 #include "editutil.hxx"
42 #include "patattr.hxx"
43 #include "xestyle.hxx"
44 #include "fprogressbar.hxx"
45 #include "xltracer.hxx"
46 #include "xecontent.hxx"
48 #include "xehelper.hxx"
50 using ::rtl::OUString
;
51 using ::com::sun::star::uno::Reference
;
52 using ::com::sun::star::i18n::XBreakIterator
;
54 // Export progress bar ========================================================
56 XclExpProgressBar::XclExpProgressBar( const XclExpRoot
& rRoot
) :
58 mxProgress( new ScfProgressBar( rRoot
.GetDocShell(), STR_SAVE_DOC
) ),
62 mnSegRowFinal( SCF_INV_SEGMENT
),
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
) )
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()
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
);
121 void XclExpProgressBar::ActivateFinalRowsSegment()
123 if( !mpSubRowFinal
&& (mnRowCount
> 0) )
125 mpSubRowFinal
= &mxProgress
->GetSegmentProgressBar( mnSegRowFinal
);
126 mpSubRowFinal
->AddSegment( mnRowCount
);
128 mpSubProgress
= mpSubRowFinal
;
130 mpSubProgress
->Activate();
133 void XclExpProgressBar::Progress()
135 if( mpSubProgress
&& !mpSubProgress
->IsFull() )
136 mpSubProgress
->Progress();
139 // Calc->Excel cell address/range conversion ==================================
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
);
152 // ----------------------------------------------------------------------------
154 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot
& rRoot
) :
155 XclAddressConverterBase( rRoot
.GetTracer(), rRoot
.GetXclMaxPos() )
159 // cell address ---------------------------------------------------------------
161 bool XclExpAddressConverter::CheckAddress( const ScAddress
& rScPos
, bool bWarn
)
163 // ScAddress::operator<=() doesn't do what we want here
164 bool bValidCol
= (0 <= rScPos
.Col()) && (rScPos
.Col() <= maMaxPos
.Col());
165 bool bValidRow
= (0 <= rScPos
.Row()) && (rScPos
.Row() <= maMaxPos
.Row());
166 bool bValidTab
= (0 <= rScPos
.Tab()) && (rScPos
.Tab() <= maMaxPos
.Tab());
168 bool bValid
= bValidCol
&& bValidRow
&& bValidTab
;
169 if( !bValid
&& bWarn
)
171 mbColTrunc
|= !bValidCol
;
172 mbRowTrunc
|= !bValidRow
;
173 mbTabTrunc
|= (rScPos
.Tab() > maMaxPos
.Tab()); // do not warn for deleted refs
174 mrTracer
.TraceInvalidAddress( rScPos
, maMaxPos
);
179 bool XclExpAddressConverter::ConvertAddress( XclAddress
& rXclPos
,
180 const ScAddress
& rScPos
, bool bWarn
)
182 bool bValid
= CheckAddress( rScPos
, bWarn
);
184 lclFillAddress( rXclPos
, rScPos
.Col(), rScPos
.Row() );
188 XclAddress
XclExpAddressConverter::CreateValidAddress( const ScAddress
& rScPos
, bool bWarn
)
190 XclAddress
aXclPos( ScAddress::UNINITIALIZED
);
191 if( !ConvertAddress( aXclPos
, rScPos
, bWarn
) )
192 lclFillAddress( aXclPos
, ::std::min( rScPos
.Col(), maMaxPos
.Col() ), ::std::min( rScPos
.Row(), maMaxPos
.Row() ) );
196 // cell range -----------------------------------------------------------------
198 bool XclExpAddressConverter::CheckRange( const ScRange
& rScRange
, bool bWarn
)
200 return CheckAddress( rScRange
.aStart
, bWarn
) && CheckAddress( rScRange
.aEnd
, bWarn
);
203 bool XclExpAddressConverter::ValidateRange( ScRange
& rScRange
, bool bWarn
)
207 // check start position
208 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
211 // check & correct end position
212 ScAddress
& rScEnd
= rScRange
.aEnd
;
213 if( !CheckAddress( rScEnd
, bWarn
) )
215 rScEnd
.SetCol( ::std::min( rScEnd
.Col(), maMaxPos
.Col() ) );
216 rScEnd
.SetRow( ::std::min( rScEnd
.Row(), maMaxPos
.Row() ) );
217 rScEnd
.SetTab( ::std::min( rScEnd
.Tab(), maMaxPos
.Tab() ) );
224 bool XclExpAddressConverter::ConvertRange( XclRange
& rXclRange
,
225 const ScRange
& rScRange
, bool bWarn
)
227 // check start position
228 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
231 lclFillAddress( rXclRange
.maFirst
, rScRange
.aStart
.Col(), rScRange
.aStart
.Row() );
233 // check & correct end position
234 SCCOL nScCol2
= rScRange
.aEnd
.Col();
235 SCROW nScRow2
= rScRange
.aEnd
.Row();
236 if( !CheckAddress( rScRange
.aEnd
, bWarn
) )
238 nScCol2
= ::std::min( nScCol2
, maMaxPos
.Col() );
239 nScRow2
= ::std::min( nScRow2
, maMaxPos
.Row() );
241 lclFillAddress( rXclRange
.maLast
, nScCol2
, nScRow2
);
246 // cell range list ------------------------------------------------------------
248 void XclExpAddressConverter::ValidateRangeList( ScRangeList
& rScRanges
, bool bWarn
)
250 for ( size_t nRange
= rScRanges
.size(); nRange
> 0; )
252 ScRange
* pScRange
= rScRanges
[ --nRange
];
253 if( !CheckRange( *pScRange
, bWarn
) )
254 delete rScRanges
.Remove(nRange
);
258 void XclExpAddressConverter::ConvertRangeList( XclRangeList
& rXclRanges
,
259 const ScRangeList
& rScRanges
, bool bWarn
)
262 for( size_t nPos
= 0, nCount
= rScRanges
.size(); nPos
< nCount
; ++nPos
)
264 if( const ScRange
* pScRange
= rScRanges
[ nPos
] )
266 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
267 if( ConvertRange( aXclRange
, *pScRange
, bWarn
) )
268 rXclRanges
.push_back( aXclRange
);
273 // EditEngine->String conversion ==============================================
277 rtl::OUString
lclGetUrlRepresentation( const SvxURLField
& rUrlField
)
279 const rtl::OUString
& aRepr
= rUrlField
.GetRepresentation();
280 // no representation -> use URL
281 return aRepr
.isEmpty() ? rUrlField
.GetURL() : aRepr
;
286 // ----------------------------------------------------------------------------
288 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot
& rRoot
, const ScAddress
& rScPos
) :
291 mbMultipleUrls( false )
295 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
299 rtl::OUString
XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField
& rUrlField
)
301 rtl::OUString aUrlRepr
;
303 if( GetBiff() == EXC_BIFF8
) // no HLINK records in BIFF2-BIFF7
305 // there was/is already a HLINK record
306 mbMultipleUrls
= mxLinkRec
;
308 mxLinkRec
.reset( new XclExpHyperlink( GetRoot(), rUrlField
, maScPos
) );
310 if( const String
* pRepr
= mxLinkRec
->GetRepr() )
313 // add URL to note text
314 maUrlList
= ScGlobal::addToken( maUrlList
, rUrlField
.GetURL(), '\n' );
317 // no hyperlink representation from Excel HLINK record -> use it from text field
318 return aUrlRepr
.isEmpty() ? lclGetUrlRepresentation(rUrlField
) : aUrlRepr
;
321 bool XclExpHyperlinkHelper::HasLinkRecord() const
323 return !mbMultipleUrls
&& mxLinkRec
;
326 XclExpHyperlinkHelper::XclExpHyperlinkRef
XclExpHyperlinkHelper::GetLinkRecord()
328 if( HasLinkRecord() )
330 return XclExpHyperlinkRef();
333 // ----------------------------------------------------------------------------
337 /** Creates a new formatted string from the passed unformatted string.
339 Creates a Unicode string or a byte string, depending on the current BIFF
340 version contained in the passed XclExpRoot object. May create a formatted
341 string object, if the text contains different script types.
344 Cell attributes used for font formatting.
346 Modifiers for string export.
348 The maximum number of characters to store in this string.
350 The new string object.
352 XclExpStringRef
lclCreateFormattedString(
353 const XclExpRoot
& rRoot
, const String
& rText
, const ScPatternAttr
* pCellAttr
,
354 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
356 /* Create an empty Excel string object with correctly initialized BIFF mode,
357 because this function only uses Append() functions that require this. */
358 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
360 // script type handling
361 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
362 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
363 // #i63255# get script type for leading weak characters
364 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rText
);
366 // font buffer and cell item set
367 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
368 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
370 // process all script portions
371 OUString
aOUText( rText
);
372 sal_Int32 nPortionPos
= 0;
373 sal_Int32 nTextLen
= aOUText
.getLength();
374 while( nPortionPos
< nTextLen
)
376 // get script type and end position of next script portion
377 sal_Int16 nScript
= xBreakIt
->getScriptType( aOUText
, nPortionPos
);
378 sal_Int32 nPortionEnd
= xBreakIt
->endOfScript( aOUText
, nPortionPos
, nScript
);
380 // reuse previous script for following weak portions
381 if( nScript
== ApiScriptType::WEAK
)
382 nScript
= nLastScript
;
384 // construct font from current text portion
385 SvxFont
aFont( XclExpFontHelper::GetFontFromItemSet( rRoot
, rItemSet
, nScript
) );
387 // Excel start position of this portion
388 sal_uInt16 nXclPortionStart
= xString
->Len();
389 // add portion text to Excel string
390 XclExpStringHelper::AppendString( *xString
, rRoot
, aOUText
.copy( nPortionPos
, nPortionEnd
- nPortionPos
) );
391 if( nXclPortionStart
< xString
->Len() )
393 // insert font into buffer
394 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
395 // insert font index into format run vector
396 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
399 // go to next script portion
400 nLastScript
= nScript
;
401 nPortionPos
= nPortionEnd
;
407 /** Creates a new formatted string from an edit engine text object.
409 Creates a Unicode string or a byte string, depending on the current BIFF
410 version contained in the passed XclExpRoot object.
413 The edit engine in use. The text object must already be set.
415 Modifiers for string export.
417 The maximum number of characters to store in this string.
419 The new string object.
421 XclExpStringRef
lclCreateFormattedString(
422 const XclExpRoot
& rRoot
, EditEngine
& rEE
, XclExpHyperlinkHelper
* pLinkHelper
,
423 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
425 /* Create an empty Excel string object with correctly initialized BIFF mode,
426 because this function only uses Append() functions that require this. */
427 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
429 // font buffer and helper item set for edit engine -> Calc item conversion
430 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
431 SfxItemSet
aItemSet( *rRoot
.GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
433 // script type handling
434 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
435 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
436 // #i63255# get script type for leading weak characters
437 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rEE
.GetText() );
439 // process all paragraphs
440 sal_uInt16 nParaCount
= rEE
.GetParagraphCount();
441 for( sal_uInt16 nPara
= 0; nPara
< nParaCount
; ++nPara
)
443 ESelection
aSel( nPara
, 0 );
444 String
aParaText( rEE
.GetText( nPara
) );
446 std::vector
<sal_uInt16
> aPosList
;
447 rEE
.GetPortions( nPara
, aPosList
);
449 // process all portions in the paragraph
450 for( std::vector
<sal_uInt16
>::const_iterator
it(aPosList
.begin()); it
!= aPosList
.end(); ++it
)
452 aSel
.nEndPos
= static_cast< xub_StrLen
>( *it
);
453 String
aXclPortionText( aParaText
, aSel
.nStartPos
, aSel
.nEndPos
- aSel
.nStartPos
);
455 aItemSet
.ClearItem();
456 SfxItemSet
aEditSet( rEE
.GetAttribs( aSel
) );
457 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
459 // get escapement value
460 short nEsc
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
).GetEsc();
462 // process text fields
463 bool bIsHyperlink
= false;
464 if( aSel
.nStartPos
+ 1 == aSel
.nEndPos
)
466 // test if the character is a text field
467 const SfxPoolItem
* pItem
;
468 if( aEditSet
.GetItemState( EE_FEATURE_FIELD
, false, &pItem
) == SFX_ITEM_SET
)
470 const SvxFieldData
* pField
= static_cast< const SvxFieldItem
* >( pItem
)->GetField();
471 if( const SvxURLField
* pUrlField
= PTR_CAST( SvxURLField
, pField
) )
473 // convert URL field to string representation
474 aXclPortionText
= pLinkHelper
?
475 pLinkHelper
->ProcessUrlField( *pUrlField
) :
476 lclGetUrlRepresentation( *pUrlField
);
481 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
482 aXclPortionText
.Erase();
487 // Excel start position of this portion
488 sal_uInt16 nXclPortionStart
= xString
->Len();
489 // add portion text to Excel string
490 XclExpStringHelper::AppendString( *xString
, rRoot
, aXclPortionText
);
491 if( (nXclPortionStart
< xString
->Len()) || (aParaText
.Len() == 0) )
493 /* Construct font from current edit engine text portion. Edit engine
494 creates different portions for different script types, no need to loop. */
495 sal_Int16 nScript
= xBreakIt
->getScriptType( aXclPortionText
, 0 );
496 if( nScript
== ApiScriptType::WEAK
)
497 nScript
= nLastScript
;
498 SvxFont
aFont( XclExpFontHelper::GetFontFromItemSet( rRoot
, aItemSet
, nScript
) );
499 nLastScript
= nScript
;
502 aFont
.SetEscapement( nEsc
);
503 // modify automatic font color for hyperlinks
504 if( bIsHyperlink
&& (GETITEM( aItemSet
, SvxColorItem
, ATTR_FONT_COLOR
).GetValue().GetColor() == COL_AUTO
) )
505 aFont
.SetColor( Color( COL_LIGHTBLUE
) );
507 // insert font into buffer
508 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
509 // insert font index into format run vector
510 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
513 aSel
.nStartPos
= aSel
.nEndPos
;
516 // add trailing newline (important for correct character index calculation)
517 if( nPara
+ 1 < nParaCount
)
518 XclExpStringHelper::AppendChar( *xString
, rRoot
, '\n' );
526 // ----------------------------------------------------------------------------
528 XclExpStringRef
XclExpStringHelper::CreateString(
529 const XclExpRoot
& rRoot
, const String
& rString
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
531 XclExpStringRef
xString( new XclExpString
);
532 if( rRoot
.GetBiff() == EXC_BIFF8
)
533 xString
->Assign( rString
, nFlags
, nMaxLen
);
535 xString
->AssignByte( rString
, rRoot
.GetTextEncoding(), nFlags
, nMaxLen
);
539 XclExpStringRef
XclExpStringHelper::CreateString(
540 const XclExpRoot
& rRoot
, sal_Unicode cChar
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
542 XclExpStringRef xString
= CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
543 AppendChar( *xString
, rRoot
, cChar
);
547 void XclExpStringHelper::AppendString( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, const String
& rString
)
549 if( rRoot
.GetBiff() == EXC_BIFF8
)
550 rXclString
.Append( rString
);
552 rXclString
.AppendByte( rString
, rRoot
.GetTextEncoding() );
555 void XclExpStringHelper::AppendChar( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, sal_Unicode cChar
)
557 if( rRoot
.GetBiff() == EXC_BIFF8
)
558 rXclString
.Append( rtl::OUString(cChar
) );
560 rXclString
.AppendByte( cChar
, rRoot
.GetTextEncoding() );
563 XclExpStringRef
XclExpStringHelper::CreateCellString(
564 const XclExpRoot
& rRoot
, const ScStringCell
& rStringCell
, const ScPatternAttr
* pCellAttr
,
565 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
567 rtl::OUString aCellText
= rStringCell
.GetString();
568 return lclCreateFormattedString( rRoot
, aCellText
, pCellAttr
, nFlags
, nMaxLen
);
571 XclExpStringRef
XclExpStringHelper::CreateCellString(
572 const XclExpRoot
& rRoot
, const ScEditCell
& rEditCell
, const ScPatternAttr
* pCellAttr
,
573 XclExpHyperlinkHelper
& rLinkHelper
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
575 XclExpStringRef xString
;
576 if( const EditTextObject
* pEditObj
= rEditCell
.GetData() )
579 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
580 sal_Bool bOldUpdateMode
= rEE
.GetUpdateMode();
581 rEE
.SetUpdateMode( sal_True
);
583 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
584 SfxItemSet
* pEEItemSet
= new SfxItemSet( rEE
.GetEmptyItemSet() );
585 ScPatternAttr::FillToEditItemSet( *pEEItemSet
, rItemSet
);
586 rEE
.SetDefaults( pEEItemSet
); // edit engine takes ownership
588 rEE
.SetText( *pEditObj
);
589 xString
= lclCreateFormattedString( rRoot
, rEE
, &rLinkHelper
, nFlags
, nMaxLen
);
590 rEE
.SetUpdateMode( bOldUpdateMode
);
595 String aCellText
= rEditCell
.GetString();
596 xString
= lclCreateFormattedString( rRoot
, aCellText
, pCellAttr
, nFlags
, nMaxLen
);
601 XclExpStringRef
XclExpStringHelper::CreateString(
602 const XclExpRoot
& rRoot
, const SdrTextObj
& rTextObj
,
603 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
605 XclExpStringRef xString
;
606 if( const OutlinerParaObject
* pParaObj
= rTextObj
.GetOutlinerParaObject() )
608 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
609 sal_Bool bOldUpdateMode
= rEE
.GetUpdateMode();
610 rEE
.SetUpdateMode( sal_True
);
612 rEE
.SetText( pParaObj
->GetTextObject() );
613 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
614 rEE
.SetUpdateMode( bOldUpdateMode
);
615 // limit formats - TODO: BIFF dependent
616 if( !xString
->IsEmpty() )
618 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
619 xString
->AppendTrailingFormat( EXC_FONT_APP
);
624 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
625 // create BIFF dependent empty Excel string
626 xString
= CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
631 XclExpStringRef
XclExpStringHelper::CreateString(
632 const XclExpRoot
& rRoot
, const EditTextObject
& rEditObj
,
633 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
635 XclExpStringRef xString
;
636 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
637 sal_Bool bOldUpdateMode
= rEE
.GetUpdateMode();
638 rEE
.SetUpdateMode( sal_True
);
639 rEE
.SetText( rEditObj
);
640 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
641 rEE
.SetUpdateMode( bOldUpdateMode
);
642 // limit formats - TODO: BIFF dependent
643 if( !xString
->IsEmpty() )
645 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
646 xString
->AppendTrailingFormat( EXC_FONT_APP
);
651 sal_Int16
XclExpStringHelper::GetLeadingScriptType( const XclExpRoot
& rRoot
, const String
& rString
)
653 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
654 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
655 OUString
aOUString( rString
);
656 sal_Int32 nStrPos
= 0;
657 sal_Int32 nStrLen
= aOUString
.getLength();
658 sal_Int16 nScript
= ApiScriptType::WEAK
;
659 while( (nStrPos
< nStrLen
) && (nScript
== ApiScriptType::WEAK
) )
661 nScript
= xBreakIt
->getScriptType( aOUString
, nStrPos
);
662 nStrPos
= xBreakIt
->endOfScript( aOUString
, nStrPos
, nScript
);
664 return (nScript
== ApiScriptType::WEAK
) ? rRoot
.GetDefApiScript() : nScript
;
667 // Header/footer conversion ===================================================
669 XclExpHFConverter::XclExpHFConverter( const XclExpRoot
& rRoot
) :
671 mrEE( rRoot
.GetHFEditEngine() ),
676 void XclExpHFConverter::GenerateString(
677 const EditTextObject
* pLeftObj
,
678 const EditTextObject
* pCenterObj
,
679 const EditTextObject
* pRightObj
)
683 AppendPortion( pLeftObj
, 'L' );
684 AppendPortion( pCenterObj
, 'C' );
685 AppendPortion( pRightObj
, 'R' );
688 void XclExpHFConverter::AppendPortion( const EditTextObject
* pTextObj
, sal_Unicode cPortionCode
)
690 if( !pTextObj
) return;
693 sal_Int32 nHeight
= 0;
694 SfxItemSet
aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
697 sal_Bool bOldUpdateMode
= mrEE
.GetUpdateMode();
698 mrEE
.SetUpdateMode( sal_True
);
699 mrEE
.SetText( *pTextObj
);
702 XclFontData aFontData
, aNewData
;
703 if( const XclExpFont
* pFirstFont
= GetFontBuffer().GetFont( EXC_FONT_APP
) )
705 aFontData
= pFirstFont
->GetFontData();
706 (aFontData
.mnHeight
+= 10) /= 20; // using pt here, not twips
709 aFontData
.mnHeight
= 10;
711 const FontList
* pFontList
= 0;
712 if( SfxObjectShell
* pDocShell
= GetDocShell() )
714 if( const SvxFontListItem
* pInfoItem
= static_cast< const SvxFontListItem
* >(
715 pDocShell
->GetItem( SID_ATTR_CHAR_FONTLIST
) ) )
716 pFontList
= pInfoItem
->GetFontList();
719 sal_uInt16 nParaCount
= mrEE
.GetParagraphCount();
720 for( sal_uInt16 nPara
= 0; nPara
< nParaCount
; ++nPara
)
722 ESelection
aSel( nPara
, 0 );
724 sal_Int32 nParaHeight
= 0;
725 std::vector
<sal_uInt16
> aPosList
;
726 mrEE
.GetPortions( nPara
, aPosList
);
728 for( std::vector
<sal_uInt16
>::const_iterator
it( aPosList
.begin() ); it
!= aPosList
.end(); ++it
)
730 aSel
.nEndPos
= static_cast< xub_StrLen
>( *it
);
731 if( aSel
.nStartPos
< aSel
.nEndPos
)
734 // --- font attributes ---
737 aItemSet
.ClearItem();
738 SfxItemSet
aEditSet( mrEE
.GetAttribs( aSel
) );
739 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
740 ScPatternAttr::GetFont( aFont
, aItemSet
, SC_AUTOCOL_RAW
);
742 // font name and style
743 aNewData
.maName
= XclTools::GetXclFontName( aFont
.GetName() );
744 aNewData
.mnWeight
= (aFont
.GetWeight() > WEIGHT_NORMAL
) ? EXC_FONTWGHT_BOLD
: EXC_FONTWGHT_NORMAL
;
745 aNewData
.mbItalic
= (aFont
.GetItalic() != ITALIC_NONE
);
746 bool bNewFont
= !(aFontData
.maName
== aNewData
.maName
);
747 bool bNewStyle
= (aFontData
.mnWeight
!= aNewData
.mnWeight
) ||
748 (aFontData
.mbItalic
!= aNewData
.mbItalic
);
749 if( bNewFont
|| (bNewStyle
&& pFontList
) )
751 aParaText
.AppendAscii( "&\"" ).Append( aNewData
.maName
);
754 FontInfo
aFontInfo( pFontList
->Get(
756 (aNewData
.mnWeight
> EXC_FONTWGHT_NORMAL
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
,
757 aNewData
.mbItalic
? ITALIC_NORMAL
: ITALIC_NONE
) );
758 aNewData
.maStyle
= pFontList
->GetStyleName( aFontInfo
);
759 if( aNewData
.maStyle
.Len() )
760 aParaText
.Append( ',' ).Append( aNewData
.maStyle
);
762 aParaText
.Append( '"' );
766 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
767 // -> get it directly from edit engine item set
768 aNewData
.mnHeight
= ulimit_cast
< sal_uInt16
>( GETITEM( aEditSet
, SvxFontHeightItem
, EE_CHAR_FONTHEIGHT
).GetHeight() );
769 (aNewData
.mnHeight
+= 10) /= 20;
770 bool bFontHtChanged
= (aFontData
.mnHeight
!= aNewData
.mnHeight
);
772 aParaText
.Append( '&' ).Append( String::CreateFromInt32( aNewData
.mnHeight
) );
773 // update maximum paragraph height, convert to twips
774 nParaHeight
= ::std::max
< sal_Int32
>( nParaHeight
, aNewData
.mnHeight
* 20 );
777 aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
;
778 switch( aFont
.GetUnderline() )
780 case UNDERLINE_NONE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
; break;
781 case UNDERLINE_SINGLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
; break;
782 case UNDERLINE_DOUBLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_DOUBLE
; break;
783 default: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
;
785 if( aFontData
.mnUnderline
!= aNewData
.mnUnderline
)
787 sal_uInt8 nTmpUnderl
= (aNewData
.mnUnderline
== EXC_FONTUNDERL_NONE
) ?
788 aFontData
.mnUnderline
: aNewData
.mnUnderline
;
789 aParaText
.AppendAscii( (nTmpUnderl
== EXC_FONTUNDERL_SINGLE
) ? "&U" : "&E" );
793 aNewData
.mbStrikeout
= (aFont
.GetStrikeout() != STRIKEOUT_NONE
);
794 if( aFontData
.mbStrikeout
!= aNewData
.mbStrikeout
)
795 aParaText
.AppendAscii( "&S" );
798 const SvxEscapementItem
& rEscapeItem
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
);
799 aNewData
.SetScEscapement( rEscapeItem
.GetEsc() );
800 if( aFontData
.mnEscapem
!= aNewData
.mnEscapem
)
802 switch(aNewData
.mnEscapem
)
804 // close the previous super/sub script.
805 case EXC_FONTESC_NONE
: aParaText
.AppendAscii( (aFontData
.mnEscapem
== EXC_FONTESC_SUPER
) ? "&X" : "&Y" ); break;
806 case EXC_FONTESC_SUPER
: aParaText
.AppendAscii( "&X" ); break;
807 case EXC_FONTESC_SUB
: aParaText
.AppendAscii( "&Y" ); break;
812 aFontData
= aNewData
;
814 // --- text content or text fields ---
816 const SfxPoolItem
* pItem
;
817 if( (aSel
.nStartPos
+ 1 == aSel
.nEndPos
) && // fields are single characters
818 (aEditSet
.GetItemState( EE_FEATURE_FIELD
, false, &pItem
) == SFX_ITEM_SET
) )
820 if( const SvxFieldData
* pFieldData
= static_cast< const SvxFieldItem
* >( pItem
)->GetField() )
822 if( pFieldData
->ISA( SvxPageField
) )
823 aParaText
.AppendAscii( "&P" );
824 else if( pFieldData
->ISA( SvxPagesField
) )
825 aParaText
.AppendAscii( "&N" );
826 else if( pFieldData
->ISA( SvxDateField
) )
827 aParaText
.AppendAscii( "&D" );
828 else if( pFieldData
->ISA( SvxTimeField
) || pFieldData
->ISA( SvxExtTimeField
) )
829 aParaText
.AppendAscii( "&T" );
830 else if( pFieldData
->ISA( SvxTableField
) )
831 aParaText
.AppendAscii( "&A" );
832 else if( pFieldData
->ISA( SvxFileField
) ) // title -> file name
833 aParaText
.AppendAscii( "&F" );
834 else if( const SvxExtFileField
* pFileField
= PTR_CAST( SvxExtFileField
, pFieldData
) )
836 switch( pFileField
->GetFormat() )
838 case SVXFILEFORMAT_NAME_EXT
:
839 case SVXFILEFORMAT_NAME
:
840 aParaText
.AppendAscii( "&F" );
842 case SVXFILEFORMAT_PATH
:
843 aParaText
.AppendAscii( "&Z" );
845 case SVXFILEFORMAT_FULLPATH
:
846 aParaText
.AppendAscii( "&Z&F" );
849 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
856 String
aPortionText( mrEE
.GetText( aSel
) );
857 aPortionText
.SearchAndReplaceAll( rtl::OUString('&'), rtl::OUString("&&") );
858 // #i17440# space between font height and numbers in text
859 if( bFontHtChanged
&& aParaText
.Len() && aPortionText
.Len() )
861 sal_Unicode cLast
= aParaText
.GetChar( aParaText
.Len() - 1 );
862 sal_Unicode cFirst
= aPortionText
.GetChar( 0 );
863 if( ('0' <= cLast
) && (cLast
<= '9') && ('0' <= cFirst
) && (cFirst
<= '9') )
864 aParaText
.Append( ' ' );
866 aParaText
.Append( aPortionText
);
870 aSel
.nStartPos
= aSel
.nEndPos
;
873 aText
= ScGlobal::addToken( aText
, aParaText
, '\n' );
874 if( nParaHeight
== 0 )
875 nParaHeight
= aFontData
.mnHeight
* 20; // points -> twips
876 nHeight
+= nParaHeight
;
879 mrEE
.SetUpdateMode( bOldUpdateMode
);
883 maHFString
.Append( '&' ).Append( cPortionCode
).Append( aText
);
884 mnTotalHeight
= ::std::max( mnTotalHeight
, nHeight
);
888 // URL conversion =============================================================
892 /** Encodes special parts of the URL, i.e. directory separators and volume names.
893 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
894 rtl::OUString
lclEncodeDosUrl(
895 XclBiff eBiff
, const rtl::OUString
& rUrl
, const rtl::OUString
& rBase
, const rtl::OUString
* pTableName
)
897 rtl::OUStringBuffer aBuf
;
901 rtl::OUString aOldUrl
= rUrl
;
902 aBuf
.append(EXC_URLSTART_ENCODED
);
904 if ( aOldUrl
.getLength() > 2 && aOldUrl
.copy(0,2) == "\\\\" )
907 aBuf
.append(EXC_URL_DOSDRIVE
).append(sal_Unicode('@'));
908 aOldUrl
= aOldUrl
.copy(2);
910 else if ( aOldUrl
.getLength() > 2 && aOldUrl
.copy(1,2) == ":\\" )
913 sal_Unicode cThisDrive
= rBase
.isEmpty() ? ' ' : rBase
.getStr()[0];
914 sal_Unicode cDrive
= aOldUrl
.getStr()[0];
915 if (cThisDrive
== cDrive
)
916 // This document and the referenced document are under the same drive.
917 aBuf
.append(EXC_URL_DRIVEROOT
);
919 aBuf
.append(EXC_URL_DOSDRIVE
).append(cDrive
);
920 aOldUrl
= aOldUrl
.copy(3);
925 while((nPos
= aOldUrl
.indexOf('\\')) != -1)
927 if ( aOldUrl
.copy(0,2) == ".." )
928 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
929 // Excel seems confused by this token).
930 aBuf
.append(EXC_URL_PARENTDIR
);
932 aBuf
.append(aOldUrl
.copy(0,nPos
)).append(EXC_URL_SUBDIR
);
934 aOldUrl
= aOldUrl
.copy(nPos
+ 1);
938 if (pTableName
) // enclose file name in brackets if table name follows
939 aBuf
.append(sal_Unicode('[')).append(aOldUrl
).append(sal_Unicode(']'));
941 aBuf
.append(aOldUrl
);
943 else // empty URL -> self reference
948 aBuf
.append(pTableName
? EXC_URLSTART_SELFENCODED
: EXC_URLSTART_SELF
);
951 DBG_ASSERT( pTableName
, "lclEncodeDosUrl - sheet name required for BIFF8" );
952 aBuf
.append(EXC_URLSTART_SELF
);
961 aBuf
.append(*pTableName
);
963 return aBuf
.makeStringAndClear();
968 // ----------------------------------------------------------------------------
970 rtl::OUString
XclExpUrlHelper::EncodeUrl( const XclExpRoot
& rRoot
, const rtl::OUString
& rAbsUrl
, const rtl::OUString
* pTableName
)
972 rtl::OUString aDosUrl
= INetURLObject(rAbsUrl
).getFSysPath(INetURLObject::FSYS_DOS
);
973 rtl::OUString aDosBase
= INetURLObject(rRoot
.GetBasePath()).getFSysPath(INetURLObject::FSYS_DOS
);
974 return lclEncodeDosUrl(rRoot
.GetBiff(), aDosUrl
, aDosBase
, pTableName
);
977 rtl::OUString
XclExpUrlHelper::EncodeDde( const rtl::OUString
& rApplic
, const rtl::OUString
& rTopic
)
979 rtl::OUStringBuffer aBuf
;
980 aBuf
.append(rApplic
).append(EXC_DDE_DELIM
).append(rTopic
);
981 return aBuf
.makeStringAndClear();
984 // Cached Value Lists =========================================================
986 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix
& rMatrix
)
987 : mrMatrix( rMatrix
)
991 XclExpCachedMatrix::~XclExpCachedMatrix()
996 void XclExpCachedMatrix::GetDimensions( SCSIZE
& nCols
, SCSIZE
& nRows
) const
998 mrMatrix
.GetDimensions( nCols
, nRows
);
1000 OSL_ENSURE( nCols
&& nRows
, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1001 OSL_ENSURE( nCols
<= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1004 sal_Size
XclExpCachedMatrix::GetSize() const
1006 SCSIZE nCols
, nRows
;
1008 GetDimensions( nCols
, nRows
);
1010 /* The returned size may be wrong if the matrix contains strings. The only
1011 effect is that the export stream has to update a wrong record size which is
1012 faster than to iterate through all cached values and calculate their sizes. */
1013 return 3 + 9 * (nCols
* nRows
);
1016 void XclExpCachedMatrix::Save( XclExpStream
& rStrm
) const
1018 SCSIZE nCols
, nRows
;
1020 GetDimensions( nCols
, nRows
);
1022 if( rStrm
.GetRoot().GetBiff() <= EXC_BIFF5
)
1023 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1024 rStrm
<< static_cast< sal_uInt8
>( nCols
) << static_cast< sal_uInt16
>( nRows
);
1026 // in BIFF8: columns and rows decreaed by 1
1027 rStrm
<< static_cast< sal_uInt8
>( nCols
- 1 ) << static_cast< sal_uInt16
>( nRows
- 1 );
1029 for( SCSIZE nRow
= 0; nRow
< nRows
; ++nRow
)
1031 for( SCSIZE nCol
= 0; nCol
< nCols
; ++nCol
)
1033 ScMatrixValue nMatVal
= mrMatrix
.Get( nCol
, nRow
);
1035 if( SC_MATVAL_EMPTY
== nMatVal
.nType
)
1037 rStrm
.SetSliceSize( 9 );
1038 rStrm
<< EXC_CACHEDVAL_EMPTY
;
1039 rStrm
.WriteZeroBytes( 8 );
1041 else if( ScMatrix::IsNonValueType( nMatVal
.nType
) )
1043 XclExpString
aStr( nMatVal
.GetString(), EXC_STR_DEFAULT
);
1044 rStrm
.SetSliceSize( 6 );
1045 rStrm
<< EXC_CACHEDVAL_STRING
<< aStr
;
1047 else if( SC_MATVAL_BOOLEAN
== nMatVal
.nType
)
1049 sal_Int8 nBool
= nMatVal
.GetBoolean();
1050 rStrm
.SetSliceSize( 9 );
1051 rStrm
<< EXC_CACHEDVAL_BOOL
<< nBool
;
1052 rStrm
.WriteZeroBytes( 7 );
1054 else if( sal_uInt16 nScError
= nMatVal
.GetError() )
1056 sal_Int8
nError ( XclTools::GetXclErrorCode( nScError
) );
1057 rStrm
.SetSliceSize( 9 );
1058 rStrm
<< EXC_CACHEDVAL_ERROR
<< nError
;
1059 rStrm
.WriteZeroBytes( 7 );
1063 rStrm
.SetSliceSize( 9 );
1064 rStrm
<< EXC_CACHEDVAL_DOUBLE
<< nMatVal
.fVal
;
1070 // ============================================================================
1072 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */