1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: xehelper.cxx,v $
10 * $Revision: 1.31.148.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
33 #include <com/sun/star/i18n/XBreakIterator.hpp>
34 #include <com/sun/star/i18n/ScriptType.hpp>
35 #include <sfx2/objsh.hxx>
36 #include <vcl/font.hxx>
37 #include <tools/urlobj.hxx>
38 #include <svtools/itemset.hxx>
39 #include <svtools/ctrltool.hxx>
40 #include <svx/svdotext.hxx>
41 #include <svx/outlobj.hxx>
42 #include "scitems.hxx"
43 #include <svx/fhgtitem.hxx>
44 #include <svx/flstitem.hxx>
45 #include <svx/colritem.hxx>
46 #include <svx/eeitem.hxx>
47 #include <svx/flditem.hxx>
48 #include <svx/escpitem.hxx>
49 #include <svx/svxfont.hxx>
51 #define _SVSTDARR_USHORTS
52 #include <svtools/svstdarr.hxx>
53 #include "document.hxx"
54 #include "docpool.hxx"
56 #include "editutil.hxx"
57 #include "patattr.hxx"
58 #include "xestyle.hxx"
59 #include "fprogressbar.hxx"
60 #include "xltracer.hxx"
61 #include "xecontent.hxx"
63 #include "xehelper.hxx"
65 using ::rtl::OUString
;
66 using ::com::sun::star::uno::Reference
;
67 using ::com::sun::star::i18n::XBreakIterator
;
69 // Export progress bar ========================================================
71 XclExpProgressBar::XclExpProgressBar( const XclExpRoot
& rRoot
) :
73 mxProgress( new ScfProgressBar( rRoot
.GetDocShell(), STR_SAVE_DOC
) ),
77 mnSegRowFinal( SCF_INV_SEGMENT
),
82 XclExpProgressBar::~XclExpProgressBar()
86 void XclExpProgressBar::Initialize()
88 const ScDocument
& rDoc
= GetDoc();
89 const XclExpTabInfo
& rTabInfo
= GetTabInfo();
90 SCTAB nScTabCount
= rTabInfo
.GetScTabCount();
92 // *** segment: creation of ROW records *** -------------------------------
94 sal_Int32 nSegRowCreate
= mxProgress
->AddSegment( 2000 );
95 mpSubRowCreate
= &mxProgress
->GetSegmentProgressBar( nSegRowCreate
);
96 maSubSegRowCreate
.resize( nScTabCount
, SCF_INV_SEGMENT
);
98 for( SCTAB nScTab
= 0; nScTab
< nScTabCount
; ++nScTab
)
100 if( rTabInfo
.IsExportTab( nScTab
) )
102 SCCOL nLastUsedScCol
;
103 SCROW nLastUsedScRow
;
104 rDoc
.GetTableArea( nScTab
, nLastUsedScCol
, nLastUsedScRow
);
105 sal_Size nSegSize
= static_cast< sal_Size
>( nLastUsedScRow
+ 1 );
106 maSubSegRowCreate
[ nScTab
] = mpSubRowCreate
->AddSegment( nSegSize
);
110 // *** segment: writing all ROW records *** -------------------------------
112 mnSegRowFinal
= mxProgress
->AddSegment( 1000 );
113 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
116 void XclExpProgressBar::IncRowRecordCount()
121 void XclExpProgressBar::ActivateCreateRowsSegment()
123 DBG_ASSERT( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
124 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
125 sal_Int32 nSeg
= maSubSegRowCreate
[ GetCurrScTab() ];
126 DBG_ASSERT( nSeg
!= SCF_INV_SEGMENT
, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
127 if( nSeg
!= SCF_INV_SEGMENT
)
129 mpSubProgress
= mpSubRowCreate
;
130 mpSubProgress
->ActivateSegment( nSeg
);
136 void XclExpProgressBar::ActivateFinalRowsSegment()
138 if( !mpSubRowFinal
&& (mnRowCount
> 0) )
140 mpSubRowFinal
= &mxProgress
->GetSegmentProgressBar( mnSegRowFinal
);
141 mpSubRowFinal
->AddSegment( mnRowCount
);
143 mpSubProgress
= mpSubRowFinal
;
145 mpSubProgress
->Activate();
148 void XclExpProgressBar::Progress()
150 if( mpSubProgress
&& !mpSubProgress
->IsFull() )
151 mpSubProgress
->Progress();
154 // Calc->Excel cell address/range conversion ==================================
158 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
159 inline void lclFillAddress( XclAddress
& rXclPos
, SCCOL nScCol
, SCROW nScRow
)
161 rXclPos
.mnCol
= static_cast< sal_uInt16
>( nScCol
);
162 rXclPos
.mnRow
= static_cast< sal_uInt16
>( nScRow
);
167 // ----------------------------------------------------------------------------
169 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot
& rRoot
) :
170 XclAddressConverterBase( rRoot
.GetTracer(), rRoot
.GetXclMaxPos() )
174 // cell address ---------------------------------------------------------------
176 bool XclExpAddressConverter::CheckAddress( const ScAddress
& rScPos
, bool bWarn
)
178 // ScAddress::operator<=() doesn't do what we want here
179 bool bValidCol
= (0 <= rScPos
.Col()) && (rScPos
.Col() <= maMaxPos
.Col());
180 bool bValidRow
= (0 <= rScPos
.Row()) && (rScPos
.Row() <= maMaxPos
.Row());
181 bool bValidTab
= (0 <= rScPos
.Tab()) && (rScPos
.Tab() <= maMaxPos
.Tab());
183 bool bValid
= bValidCol
&& bValidRow
&& bValidTab
;
184 if( !bValid
&& bWarn
)
186 mbColTrunc
|= !bValidCol
;
187 mbRowTrunc
|= !bValidRow
;
188 mbTabTrunc
|= (rScPos
.Tab() > maMaxPos
.Tab()); // do not warn for deleted refs
189 mrTracer
.TraceInvalidAddress( rScPos
, maMaxPos
);
194 bool XclExpAddressConverter::ConvertAddress( XclAddress
& rXclPos
,
195 const ScAddress
& rScPos
, bool bWarn
)
197 bool bValid
= CheckAddress( rScPos
, bWarn
);
199 lclFillAddress( rXclPos
, rScPos
.Col(), rScPos
.Row() );
203 XclAddress
XclExpAddressConverter::CreateValidAddress( const ScAddress
& rScPos
, bool bWarn
)
205 XclAddress
aXclPos( ScAddress::UNINITIALIZED
);
206 if( !ConvertAddress( aXclPos
, rScPos
, bWarn
) )
207 lclFillAddress( aXclPos
, ::std::min( rScPos
.Col(), maMaxPos
.Col() ), ::std::min( rScPos
.Row(), maMaxPos
.Row() ) );
211 // cell range -----------------------------------------------------------------
213 bool XclExpAddressConverter::CheckRange( const ScRange
& rScRange
, bool bWarn
)
215 return CheckAddress( rScRange
.aStart
, bWarn
) && CheckAddress( rScRange
.aEnd
, bWarn
);
218 bool XclExpAddressConverter::ValidateRange( ScRange
& rScRange
, bool bWarn
)
222 // check start position
223 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
226 // check & correct end position
227 ScAddress
& rScEnd
= rScRange
.aEnd
;
228 if( !CheckAddress( rScEnd
, bWarn
) )
230 rScEnd
.SetCol( ::std::min( rScEnd
.Col(), maMaxPos
.Col() ) );
231 rScEnd
.SetRow( ::std::min( rScEnd
.Row(), maMaxPos
.Row() ) );
232 rScEnd
.SetTab( ::std::min( rScEnd
.Tab(), maMaxPos
.Tab() ) );
239 bool XclExpAddressConverter::ConvertRange( XclRange
& rXclRange
,
240 const ScRange
& rScRange
, bool bWarn
)
242 // check start position
243 bool bValidStart
= CheckAddress( rScRange
.aStart
, bWarn
);
246 lclFillAddress( rXclRange
.maFirst
, rScRange
.aStart
.Col(), rScRange
.aStart
.Row() );
248 // check & correct end position
249 SCCOL nScCol2
= rScRange
.aEnd
.Col();
250 SCROW nScRow2
= rScRange
.aEnd
.Row();
251 if( !CheckAddress( rScRange
.aEnd
, bWarn
) )
253 nScCol2
= ::std::min( nScCol2
, maMaxPos
.Col() );
254 nScRow2
= ::std::min( nScRow2
, maMaxPos
.Row() );
256 lclFillAddress( rXclRange
.maLast
, nScCol2
, nScRow2
);
261 //UNUSED2008-05 XclRange XclExpAddressConverter::CreateValidRange( const ScRange& rScRange, bool bWarn )
263 //UNUSED2008-05 return XclRange(
264 //UNUSED2008-05 CreateValidAddress( rScRange.aStart, bWarn ),
265 //UNUSED2008-05 CreateValidAddress( rScRange.aEnd, bWarn ) );
268 // cell range list ------------------------------------------------------------
270 //UNUSED2008-05 bool XclExpAddressConverter::CheckRangeList( const ScRangeList& rScRanges, bool bWarn )
272 //UNUSED2008-05 for( ULONG nIdx = 0, nSize = rScRanges.Count(); nIdx < nSize; ++nIdx )
273 //UNUSED2008-05 if( const ScRange* pScRange = rScRanges.GetObject( nIdx ) )
274 //UNUSED2008-05 if( !CheckRange( *pScRange, bWarn ) )
275 //UNUSED2008-05 return false;
276 //UNUSED2008-05 return true;
279 void XclExpAddressConverter::ValidateRangeList( ScRangeList
& rScRanges
, bool bWarn
)
281 ULONG nIdx
= rScRanges
.Count();
284 --nIdx
; // backwards to keep nIdx valid
285 ScRange
* pScRange
= rScRanges
.GetObject( nIdx
);
286 if( pScRange
&& !CheckRange( *pScRange
, bWarn
) )
287 delete rScRanges
.Remove( nIdx
);
291 void XclExpAddressConverter::ConvertRangeList( XclRangeList
& rXclRanges
,
292 const ScRangeList
& rScRanges
, bool bWarn
)
295 for( ULONG nPos
= 0, nCount
= rScRanges
.Count(); nPos
< nCount
; ++nPos
)
297 if( const ScRange
* pScRange
= rScRanges
.GetObject( nPos
) )
299 XclRange
aXclRange( ScAddress::UNINITIALIZED
);
300 if( ConvertRange( aXclRange
, *pScRange
, bWarn
) )
301 rXclRanges
.push_back( aXclRange
);
306 // EditEngine->String conversion ==============================================
310 String
lclGetUrlRepresentation( const SvxURLField
& rUrlField
)
312 String
aRepr( rUrlField
.GetRepresentation() );
313 // no representation -> use URL
314 return aRepr
.Len() ? aRepr
: rUrlField
.GetURL();
319 // ----------------------------------------------------------------------------
321 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot
& rRoot
, const ScAddress
& rScPos
) :
324 mbMultipleUrls( false )
328 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
332 String
XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField
& rUrlField
)
336 if( GetBiff() == EXC_BIFF8
) // no HLINK records in BIFF2-BIFF7
338 // there was/is already a HLINK record
339 mbMultipleUrls
= mxLinkRec
.is();
341 mxLinkRec
.reset( new XclExpHyperlink( GetRoot(), rUrlField
, maScPos
) );
343 if( const String
* pRepr
= mxLinkRec
->GetRepr() )
346 // add URL to note text
347 ScGlobal::AddToken( maUrlList
, rUrlField
.GetURL(), '\n' );
350 // no hyperlink representation from Excel HLINK record -> use it from text field
351 return aUrlRepr
.Len() ? aUrlRepr
: lclGetUrlRepresentation( rUrlField
);
354 bool XclExpHyperlinkHelper::HasLinkRecord() const
356 return !mbMultipleUrls
&& mxLinkRec
.is();
359 XclExpHyperlinkHelper::XclExpHyperlinkRef
XclExpHyperlinkHelper::GetLinkRecord()
361 if( HasLinkRecord() )
363 return XclExpHyperlinkRef();
366 // ----------------------------------------------------------------------------
370 /** Creates a new formatted string from the passed unformatted string.
372 Creates a Unicode string or a byte string, depending on the current BIFF
373 version contained in the passed XclExpRoot object. May create a formatted
374 string object, if the text contains different script types.
377 Cell attributes used for font formatting.
379 Modifiers for string export.
381 The maximum number of characters to store in this string.
383 The new string object.
385 XclExpStringRef
lclCreateFormattedString(
386 const XclExpRoot
& rRoot
, const String
& rText
, const ScPatternAttr
* pCellAttr
,
387 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
389 /* Create an empty Excel string object with correctly initialized BIFF mode,
390 because this function only uses Append() functions that require this. */
391 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
393 // script type handling
394 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
395 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
396 // #i63255# get script type for leading weak characters
397 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rText
);
399 // font buffer and cell item set
400 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
401 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
403 // process all script portions
404 OUString
aOUText( rText
);
405 sal_Int32 nPortionPos
= 0;
406 sal_Int32 nTextLen
= aOUText
.getLength();
407 while( nPortionPos
< nTextLen
)
409 // get script type and end position of next script portion
410 sal_Int16 nScript
= xBreakIt
->getScriptType( aOUText
, nPortionPos
);
411 sal_Int32 nPortionEnd
= xBreakIt
->endOfScript( aOUText
, nPortionPos
, nScript
);
413 // reuse previous script for following weak portions
414 if( nScript
== ApiScriptType::WEAK
)
415 nScript
= nLastScript
;
417 // construct font from current text portion
418 SvxFont
aFont( XclExpFontBuffer::GetFontFromItemSet( rItemSet
, nScript
) );
420 // Excel start position of this portion
421 sal_uInt16 nXclPortionStart
= xString
->Len();
422 // add portion text to Excel string
423 XclExpStringHelper::AppendString( *xString
, rRoot
, aOUText
.copy( nPortionPos
, nPortionEnd
- nPortionPos
) );
424 if( nXclPortionStart
< xString
->Len() )
426 // insert font into buffer
427 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
428 // insert font index into format run vector
429 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
432 // go to next script portion
433 nLastScript
= nScript
;
434 nPortionPos
= nPortionEnd
;
440 /** Creates a new formatted string from an edit engine text object.
442 Creates a Unicode string or a byte string, depending on the current BIFF
443 version contained in the passed XclExpRoot object.
446 The edit engine in use. The text object must already be set.
448 Modifiers for string export.
450 The maximum number of characters to store in this string.
452 The new string object.
454 XclExpStringRef
lclCreateFormattedString(
455 const XclExpRoot
& rRoot
, EditEngine
& rEE
, XclExpHyperlinkHelper
* pLinkHelper
,
456 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
458 /* Create an empty Excel string object with correctly initialized BIFF mode,
459 because this function only uses Append() functions that require this. */
460 XclExpStringRef xString
= XclExpStringHelper::CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
462 // font buffer and helper item set for edit engine -> Calc item conversion
463 XclExpFontBuffer
& rFontBuffer
= rRoot
.GetFontBuffer();
464 SfxItemSet
aItemSet( *rRoot
.GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
466 // script type handling
467 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
468 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
469 // #i63255# get script type for leading weak characters
470 sal_Int16 nLastScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, rEE
.GetText() );
472 // process all paragraphs
473 sal_uInt16 nParaCount
= rEE
.GetParagraphCount();
474 for( sal_uInt16 nPara
= 0; nPara
< nParaCount
; ++nPara
)
476 ESelection
aSel( nPara
, 0 );
477 String
aParaText( rEE
.GetText( nPara
) );
480 rEE
.GetPortions( nPara
, aPosList
);
482 // process all portions in the paragraph
483 sal_uInt16 nPosCount
= aPosList
.Count();
484 for( sal_uInt16 nPos
= 0; nPos
< nPosCount
; ++nPos
)
486 aSel
.nEndPos
= static_cast< xub_StrLen
>( aPosList
.GetObject( nPos
) );
487 String
aXclPortionText( aParaText
, aSel
.nStartPos
, aSel
.nEndPos
- aSel
.nStartPos
);
489 aItemSet
.ClearItem();
490 SfxItemSet
aEditSet( rEE
.GetAttribs( aSel
) );
491 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
493 // get escapement value
494 short nEsc
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
).GetEsc();
496 // process text fields
497 bool bIsHyperlink
= false;
498 if( aSel
.nStartPos
+ 1 == aSel
.nEndPos
)
500 // test if the character is a text field
501 const SfxPoolItem
* pItem
;
502 if( aEditSet
.GetItemState( EE_FEATURE_FIELD
, FALSE
, &pItem
) == SFX_ITEM_SET
)
504 const SvxFieldData
* pField
= static_cast< const SvxFieldItem
* >( pItem
)->GetField();
505 if( const SvxURLField
* pUrlField
= PTR_CAST( SvxURLField
, pField
) )
507 // convert URL field to string representation
508 aXclPortionText
= pLinkHelper
?
509 pLinkHelper
->ProcessUrlField( *pUrlField
) :
510 lclGetUrlRepresentation( *pUrlField
);
515 DBG_ERRORFILE( "lclCreateFormattedString - unknown text field" );
516 aXclPortionText
.Erase();
521 // Excel start position of this portion
522 sal_uInt16 nXclPortionStart
= xString
->Len();
523 // add portion text to Excel string
524 XclExpStringHelper::AppendString( *xString
, rRoot
, aXclPortionText
);
525 if( (nXclPortionStart
< xString
->Len()) || (aParaText
.Len() == 0) )
527 /* Construct font from current edit engine text portion. Edit engine
528 creates different portions for different script types, no need to loop. */
529 sal_Int16 nScript
= xBreakIt
->getScriptType( aXclPortionText
, 0 );
530 if( nScript
== ApiScriptType::WEAK
)
531 nScript
= nLastScript
;
532 SvxFont
aFont( XclExpFontBuffer::GetFontFromItemSet( aItemSet
, nScript
) );
533 nLastScript
= nScript
;
536 aFont
.SetEscapement( nEsc
);
537 // modify automatic font color for hyperlinks
538 if( bIsHyperlink
&& (GETITEM( aItemSet
, SvxColorItem
, ATTR_FONT_COLOR
).GetValue().GetColor() == COL_AUTO
) )
539 aFont
.SetColor( Color( COL_LIGHTBLUE
) );
541 // insert font into buffer
542 sal_uInt16 nFontIdx
= rFontBuffer
.Insert( aFont
, EXC_COLOR_CELLTEXT
);
543 // insert font index into format run vector
544 xString
->AppendFormat( nXclPortionStart
, nFontIdx
);
547 aSel
.nStartPos
= aSel
.nEndPos
;
550 // add trailing newline (important for correct character index calculation)
551 if( nPara
+ 1 < nParaCount
)
552 XclExpStringHelper::AppendChar( *xString
, rRoot
, '\n' );
560 // ----------------------------------------------------------------------------
562 XclExpStringRef
XclExpStringHelper::CreateString(
563 const XclExpRoot
& rRoot
, const String
& rString
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
565 XclExpStringRef
xString( new XclExpString
);
566 if( rRoot
.GetBiff() == EXC_BIFF8
)
567 xString
->Assign( rString
, nFlags
, nMaxLen
);
569 xString
->AssignByte( rString
, rRoot
.GetTextEncoding(), nFlags
, nMaxLen
);
573 XclExpStringRef
XclExpStringHelper::CreateString(
574 const XclExpRoot
& rRoot
, sal_Unicode cChar
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
576 XclExpStringRef xString
= CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
577 AppendChar( *xString
, rRoot
, cChar
);
581 void XclExpStringHelper::AppendString( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, const String
& rString
)
583 if( rRoot
.GetBiff() == EXC_BIFF8
)
584 rXclString
.Append( rString
);
586 rXclString
.AppendByte( rString
, rRoot
.GetTextEncoding() );
589 void XclExpStringHelper::AppendChar( XclExpString
& rXclString
, const XclExpRoot
& rRoot
, sal_Unicode cChar
)
591 if( rRoot
.GetBiff() == EXC_BIFF8
)
592 rXclString
.Append( cChar
);
594 rXclString
.AppendByte( cChar
, rRoot
.GetTextEncoding() );
597 XclExpStringRef
XclExpStringHelper::CreateCellString(
598 const XclExpRoot
& rRoot
, const ScStringCell
& rStringCell
, const ScPatternAttr
* pCellAttr
,
599 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
602 rStringCell
.GetString( aCellText
);
603 XclExpStringRef xString
= lclCreateFormattedString( rRoot
, aCellText
, pCellAttr
, nFlags
, nMaxLen
);
604 if (rStringCell
.HasPhonetic())
606 ScPhonetic aPhonetic
;
607 static_cast<const ScAsianStringCell
&>(rStringCell
).GetPhonetic(aPhonetic
);
608 if ( !aPhonetic
.IsEmpty() )
609 xString
->SetPhoneticSettings(aPhonetic
);
615 XclExpStringRef
XclExpStringHelper::CreateCellString(
616 const XclExpRoot
& rRoot
, const ScEditCell
& rEditCell
, const ScPatternAttr
* pCellAttr
,
617 XclExpHyperlinkHelper
& rLinkHelper
, XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
619 XclExpStringRef xString
;
620 if( const EditTextObject
* pEditObj
= rEditCell
.GetData() )
623 ScEditEngineDefaulter
& rEE
= rRoot
.GetEditEngine();
624 BOOL bOldUpdateMode
= rEE
.GetUpdateMode();
625 rEE
.SetUpdateMode( TRUE
);
627 const SfxItemSet
& rItemSet
= pCellAttr
? pCellAttr
->GetItemSet() : rRoot
.GetDoc().GetDefPattern()->GetItemSet();
628 SfxItemSet
* pEEItemSet
= new SfxItemSet( rEE
.GetEmptyItemSet() );
629 ScPatternAttr::FillToEditItemSet( *pEEItemSet
, rItemSet
);
630 rEE
.SetDefaults( pEEItemSet
); // edit engine takes ownership
632 rEE
.SetText( *pEditObj
);
633 xString
= lclCreateFormattedString( rRoot
, rEE
, &rLinkHelper
, nFlags
, nMaxLen
);
634 rEE
.SetUpdateMode( bOldUpdateMode
);
640 rEditCell
.GetString( aCellText
);
641 xString
= lclCreateFormattedString( rRoot
, aCellText
, pCellAttr
, nFlags
, nMaxLen
);
644 if (rEditCell
.HasPhonetic())
646 ScPhonetic aPhonetic
;
647 static_cast<const ScAsianEditCell
&>(rEditCell
).GetPhonetic(aPhonetic
);
648 if ( !aPhonetic
.IsEmpty() )
649 xString
->SetPhoneticSettings(aPhonetic
);
655 XclExpStringRef
XclExpStringHelper::CreateString(
656 const XclExpRoot
& rRoot
, const SdrTextObj
& rTextObj
,
657 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
659 XclExpStringRef xString
;
660 if( const OutlinerParaObject
* pParaObj
= rTextObj
.GetOutlinerParaObject() )
662 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
663 BOOL bOldUpdateMode
= rEE
.GetUpdateMode();
664 rEE
.SetUpdateMode( TRUE
);
666 rEE
.SetText( pParaObj
->GetTextObject() );
667 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
668 rEE
.SetUpdateMode( bOldUpdateMode
);
669 // limit formats - TODO: BIFF dependent
670 if( !xString
->IsEmpty() )
672 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
673 xString
->AppendTrailingFormat( EXC_FONT_APP
);
678 DBG_ERRORFILE( "XclExpStringHelper::CreateString - textbox without para object" );
679 // create BIFF dependent empty Excel string
680 xString
= CreateString( rRoot
, EMPTY_STRING
, nFlags
, nMaxLen
);
685 XclExpStringRef
XclExpStringHelper::CreateString(
686 const XclExpRoot
& rRoot
, const EditTextObject
& rEditObj
,
687 XclStrFlags nFlags
, sal_uInt16 nMaxLen
)
689 XclExpStringRef xString
;
690 EditEngine
& rEE
= rRoot
.GetDrawEditEngine();
691 BOOL bOldUpdateMode
= rEE
.GetUpdateMode();
692 rEE
.SetUpdateMode( TRUE
);
693 rEE
.SetText( rEditObj
);
694 xString
= lclCreateFormattedString( rRoot
, rEE
, 0, nFlags
, nMaxLen
);
695 rEE
.SetUpdateMode( bOldUpdateMode
);
696 // limit formats - TODO: BIFF dependent
697 if( !xString
->IsEmpty() )
699 xString
->LimitFormatCount( EXC_MAXRECSIZE_BIFF8
/ 8 - 1 );
700 xString
->AppendTrailingFormat( EXC_FONT_APP
);
705 sal_Int16
XclExpStringHelper::GetLeadingScriptType( const XclExpRoot
& rRoot
, const String
& rString
)
707 namespace ApiScriptType
= ::com::sun::star::i18n::ScriptType
;
708 Reference
< XBreakIterator
> xBreakIt
= rRoot
.GetDoc().GetBreakIterator();
709 OUString
aOUString( rString
);
710 sal_Int32 nStrPos
= 0;
711 sal_Int32 nStrLen
= aOUString
.getLength();
712 sal_Int16 nScript
= ApiScriptType::WEAK
;
713 while( (nStrPos
< nStrLen
) && (nScript
== ApiScriptType::WEAK
) )
715 nScript
= xBreakIt
->getScriptType( aOUString
, nStrPos
);
716 nStrPos
= xBreakIt
->endOfScript( aOUString
, nStrPos
, nScript
);
718 return (nScript
== ApiScriptType::WEAK
) ? rRoot
.GetDefApiScript() : nScript
;
721 // Header/footer conversion ===================================================
723 XclExpHFConverter::XclExpHFConverter( const XclExpRoot
& rRoot
) :
725 mrEE( rRoot
.GetHFEditEngine() ),
730 void XclExpHFConverter::GenerateString(
731 const EditTextObject
* pLeftObj
,
732 const EditTextObject
* pCenterObj
,
733 const EditTextObject
* pRightObj
)
737 AppendPortion( pLeftObj
, 'L' );
738 AppendPortion( pCenterObj
, 'C' );
739 AppendPortion( pRightObj
, 'R' );
742 void XclExpHFConverter::AppendPortion( const EditTextObject
* pTextObj
, sal_Unicode cPortionCode
)
744 if( !pTextObj
) return;
747 sal_Int32 nHeight
= 0;
748 SfxItemSet
aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START
, ATTR_PATTERN_END
);
751 BOOL bOldUpdateMode
= mrEE
.GetUpdateMode();
752 mrEE
.SetUpdateMode( TRUE
);
753 mrEE
.SetText( *pTextObj
);
756 XclFontData aFontData
, aNewData
;
757 if( const XclExpFont
* pFirstFont
= GetFontBuffer().GetFont( EXC_FONT_APP
) )
759 aFontData
= pFirstFont
->GetFontData();
760 (aFontData
.mnHeight
+= 10) /= 20; // using pt here, not twips
763 aFontData
.mnHeight
= 10;
765 const FontList
* pFontList
= 0;
766 if( SfxObjectShell
* pDocShell
= GetDocShell() )
768 if( const SvxFontListItem
* pInfoItem
= static_cast< const SvxFontListItem
* >(
769 pDocShell
->GetItem( SID_ATTR_CHAR_FONTLIST
) ) )
770 pFontList
= pInfoItem
->GetFontList();
773 sal_uInt16 nParaCount
= mrEE
.GetParagraphCount();
774 for( sal_uInt16 nPara
= 0; nPara
< nParaCount
; ++nPara
)
776 ESelection
aSel( nPara
, 0 );
778 sal_Int32 nParaHeight
= 0;
780 mrEE
.GetPortions( nPara
, aPosList
);
782 sal_uInt16 nPosCount
= aPosList
.Count();
783 for( sal_uInt16 nPos
= 0; nPos
< nPosCount
; ++nPos
)
785 aSel
.nEndPos
= static_cast< xub_StrLen
>( aPosList
.GetObject( nPos
) );
786 if( aSel
.nStartPos
< aSel
.nEndPos
)
789 // --- font attributes ---
792 aItemSet
.ClearItem();
793 SfxItemSet
aEditSet( mrEE
.GetAttribs( aSel
) );
794 ScPatternAttr::GetFromEditItemSet( aItemSet
, aEditSet
);
795 ScPatternAttr::GetFont( aFont
, aItemSet
, SC_AUTOCOL_RAW
);
797 // font name and style
798 aNewData
.maName
= XclTools::GetXclFontName( aFont
.GetName() );
799 aNewData
.mnWeight
= (aFont
.GetWeight() > WEIGHT_NORMAL
) ? EXC_FONTWGHT_BOLD
: EXC_FONTWGHT_NORMAL
;
800 aNewData
.mbItalic
= (aFont
.GetItalic() != ITALIC_NONE
);
801 bool bNewFont
= !(aFontData
.maName
== aNewData
.maName
);
802 bool bNewStyle
= (aFontData
.mnWeight
!= aNewData
.mnWeight
) ||
803 (aFontData
.mbItalic
!= aNewData
.mbItalic
);
804 if( bNewFont
|| (bNewStyle
&& pFontList
) )
806 aParaText
.AppendAscii( "&\"" ).Append( aNewData
.maName
);
809 FontInfo
aFontInfo( pFontList
->Get(
811 (aNewData
.mnWeight
> EXC_FONTWGHT_NORMAL
) ? WEIGHT_BOLD
: WEIGHT_NORMAL
,
812 aNewData
.mbItalic
? ITALIC_NORMAL
: ITALIC_NONE
) );
813 aNewData
.maStyle
= pFontList
->GetStyleName( aFontInfo
);
814 if( aNewData
.maStyle
.Len() )
815 aParaText
.Append( ',' ).Append( aNewData
.maStyle
);
817 aParaText
.Append( '"' );
821 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
822 // -> get it directly from edit engine item set
823 aNewData
.mnHeight
= ulimit_cast
< sal_uInt16
>( GETITEM( aEditSet
, SvxFontHeightItem
, EE_CHAR_FONTHEIGHT
).GetHeight() );
824 (aNewData
.mnHeight
+= 10) /= 20;
825 bool bFontHtChanged
= (aFontData
.mnHeight
!= aNewData
.mnHeight
);
827 aParaText
.Append( '&' ).Append( String::CreateFromInt32( aNewData
.mnHeight
) );
828 // update maximum paragraph height, convert to twips
829 nParaHeight
= ::std::max
< sal_Int32
>( nParaHeight
, aNewData
.mnHeight
* 20 );
832 aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
;
833 switch( aFont
.GetUnderline() )
835 case UNDERLINE_NONE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_NONE
; break;
836 case UNDERLINE_SINGLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
; break;
837 case UNDERLINE_DOUBLE
: aNewData
.mnUnderline
= EXC_FONTUNDERL_DOUBLE
; break;
838 default: aNewData
.mnUnderline
= EXC_FONTUNDERL_SINGLE
;
840 if( aFontData
.mnUnderline
!= aNewData
.mnUnderline
)
842 sal_uInt8 nTmpUnderl
= (aNewData
.mnUnderline
== EXC_FONTUNDERL_NONE
) ?
843 aFontData
.mnUnderline
: aNewData
.mnUnderline
;
844 aParaText
.AppendAscii( (nTmpUnderl
== EXC_FONTUNDERL_SINGLE
) ? "&U" : "&E" );
848 aNewData
.mbStrikeout
= (aFont
.GetStrikeout() != STRIKEOUT_NONE
);
849 if( aFontData
.mbStrikeout
!= aNewData
.mbStrikeout
)
850 aParaText
.AppendAscii( "&S" );
853 const SvxEscapementItem
& rEscapeItem
= GETITEM( aEditSet
, SvxEscapementItem
, EE_CHAR_ESCAPEMENT
);
854 aNewData
.SetScEscapement( rEscapeItem
.GetEsc() );
855 if( aFontData
.mnEscapem
!= aNewData
.mnEscapem
)
857 switch(aNewData
.mnEscapem
)
859 // close the previous super/sub script.
860 case EXC_FONTESC_NONE
: aParaText
.AppendAscii( (aFontData
.mnEscapem
== EXC_FONTESC_SUPER
) ? "&X" : "&Y" ); break;
861 case EXC_FONTESC_SUPER
: aParaText
.AppendAscii( "&X" ); break;
862 case EXC_FONTESC_SUB
: aParaText
.AppendAscii( "&Y" ); break;
867 aFontData
= aNewData
;
869 // --- text content or text fields ---
871 const SfxPoolItem
* pItem
;
872 if( (aSel
.nStartPos
+ 1 == aSel
.nEndPos
) && // fields are single characters
873 (aEditSet
.GetItemState( EE_FEATURE_FIELD
, sal_False
, &pItem
) == SFX_ITEM_SET
) )
875 if( const SvxFieldData
* pFieldData
= static_cast< const SvxFieldItem
* >( pItem
)->GetField() )
877 if( pFieldData
->ISA( SvxPageField
) )
878 aParaText
.AppendAscii( "&P" );
879 else if( pFieldData
->ISA( SvxPagesField
) )
880 aParaText
.AppendAscii( "&N" );
881 else if( pFieldData
->ISA( SvxDateField
) )
882 aParaText
.AppendAscii( "&D" );
883 else if( pFieldData
->ISA( SvxTimeField
) || pFieldData
->ISA( SvxExtTimeField
) )
884 aParaText
.AppendAscii( "&T" );
885 else if( pFieldData
->ISA( SvxTableField
) )
886 aParaText
.AppendAscii( "&A" );
887 else if( pFieldData
->ISA( SvxFileField
) ) // title -> file name
888 aParaText
.AppendAscii( "&F" );
889 else if( const SvxExtFileField
* pFileField
= PTR_CAST( SvxExtFileField
, pFieldData
) )
891 switch( pFileField
->GetFormat() )
893 case SVXFILEFORMAT_NAME_EXT
:
894 case SVXFILEFORMAT_NAME
:
895 aParaText
.AppendAscii( "&F" );
897 case SVXFILEFORMAT_PATH
:
898 aParaText
.AppendAscii( "&Z" );
900 case SVXFILEFORMAT_FULLPATH
:
901 aParaText
.AppendAscii( "&Z&F" );
904 DBG_ERRORFILE( "XclExpHFConverter::AppendPortion - unknown file field" );
911 String
aPortionText( mrEE
.GetText( aSel
) );
912 aPortionText
.SearchAndReplaceAll( String( '&' ), String( RTL_CONSTASCII_USTRINGPARAM( "&&" ) ) );
913 // #i17440# space between font height and numbers in text
914 if( bFontHtChanged
&& aParaText
.Len() && aPortionText
.Len() )
916 sal_Unicode cLast
= aParaText
.GetChar( aParaText
.Len() - 1 );
917 sal_Unicode cFirst
= aPortionText
.GetChar( 0 );
918 if( ('0' <= cLast
) && (cLast
<= '9') && ('0' <= cFirst
) && (cFirst
<= '9') )
919 aParaText
.Append( ' ' );
921 aParaText
.Append( aPortionText
);
925 aSel
.nStartPos
= aSel
.nEndPos
;
928 ScGlobal::AddToken( aText
, aParaText
, '\n' );
929 if( nParaHeight
== 0 )
930 nParaHeight
= aFontData
.mnHeight
* 20; // points -> twips
931 nHeight
+= nParaHeight
;
934 mrEE
.SetUpdateMode( bOldUpdateMode
);
938 maHFString
.Append( '&' ).Append( cPortionCode
).Append( aText
);
939 mnTotalHeight
= ::std::max( mnTotalHeight
, nHeight
);
943 // URL conversion =============================================================
947 /** Converts the file URL passed in rUrl to a URL in DOS notation (local or UNC).
948 @param rUrl (in/out-param) In: URL to convert; Out: Converted URL in DOS notation.
949 @param rBasePath Base path for relative URLs.
950 @param bSaveRelUrl Converts to a relative URL, using rBasePath.
951 @return True = Conversion successful, rUrl contains converted file URL. */
952 bool lclConvertToDos( String
& rUrl
, const String
& rBasePath
, bool bSaveRelUrl
)
954 String
aDosUrl( INetURLObject( rUrl
).getFSysPath( INetURLObject::FSYS_DOS
) );
955 bool bRet
= (aDosUrl
.Len() > 0);
956 if( bRet
&& bSaveRelUrl
)
958 // try to convert to relative path
959 String
aDosBase( INetURLObject( rBasePath
).getFSysPath( INetURLObject::FSYS_DOS
) );
964 // --- 1st step: delete equal subdirectories ---
966 // special handling for UNC
967 xub_StrLen nStartSearch
= aDosBase
.EqualsAscii( "\\\\", 0, 2 ) ? 2 : 0;
968 bool bEqualBase
= false;
970 while( bLoop
&& ((nPos
= aDosBase
.Search( '\\', nStartSearch
)) != STRING_NOTFOUND
) )
972 bLoop
= (TRUE
== aDosBase
.Equals( aDosUrl
, 0, nPos
+ 1 ));
975 aDosBase
.Erase( 0, nPos
+ 1 );
976 aDosUrl
.Erase( 0, nPos
+ 1 );
982 // --- 2nd step: add parent directory levels ---
986 while( (nPos
= aDosBase
.Search( '\\' )) != STRING_NOTFOUND
)
988 aDosBase
.Erase( 0, nPos
+ 1 );
989 aDosUrl
.InsertAscii( "..\\", 0 );
998 /** Encodes special parts of the URL, i.e. directory separators and volume names.
999 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
1000 void lclEncodeDosUrl( XclBiff eBiff
, String
& rUrl
, const String
* pTableName
= 0 )
1004 String
aOldUrl( rUrl
);
1005 rUrl
= EXC_URLSTART_ENCODED
;
1007 if( (aOldUrl
.Len() > 2) && aOldUrl
.EqualsAscii( "\\\\", 0, 2 ) )
1010 rUrl
.Append( EXC_URL_DOSDRIVE
).Append( '@' );
1011 aOldUrl
.Erase( 0, 2 );
1013 else if( (aOldUrl
.Len() > 2) && aOldUrl
.EqualsAscii( ":\\", 1, 2 ) )
1016 rUrl
.Append( EXC_URL_DOSDRIVE
).Append( aOldUrl
.GetChar( 0 ) );
1017 aOldUrl
.Erase( 0, 3 );
1022 while( (nPos
= aOldUrl
.Search( '\\' )) != STRING_NOTFOUND
)
1024 if( aOldUrl
.EqualsAscii( "..", 0, 2 ) )
1025 rUrl
.Append( EXC_URL_PARENTDIR
); // parent dir
1027 rUrl
.Append( aOldUrl
.GetBuffer(), nPos
).Append( EXC_URL_SUBDIR
);
1028 aOldUrl
.Erase( 0, nPos
+ 1 );
1032 if( pTableName
) // enclose file name in brackets if table name follows
1033 rUrl
.Append( '[' ).Append( aOldUrl
).Append( ']' );
1035 rUrl
.Append( aOldUrl
);
1037 else // empty URL -> self reference
1042 rUrl
= pTableName
? EXC_URLSTART_SELFENCODED
: EXC_URLSTART_SELF
;
1045 DBG_ASSERT( pTableName
, "lclEncodeDosUrl - sheet name required for BIFF8" );
1046 rUrl
= EXC_URLSTART_SELF
;
1055 rUrl
.Append( *pTableName
);
1060 // ----------------------------------------------------------------------------
1062 String
XclExpUrlHelper::EncodeUrl( const XclExpRoot
& rRoot
, const String
& rAbsUrl
, const String
* pTableName
)
1064 String
aDosUrl( rAbsUrl
);
1065 if( !aDosUrl
.Len() || lclConvertToDos( aDosUrl
, rRoot
.GetBasePath(), rRoot
.IsRelUrl() ) )
1066 lclEncodeDosUrl( rRoot
.GetBiff(), aDosUrl
, pTableName
);
1070 String
XclExpUrlHelper::EncodeDde( const String
& rApplic
, const String rTopic
)
1072 String
aDde( rApplic
);
1073 aDde
.Append( EXC_DDE_DELIM
).Append( rTopic
);
1077 // Cached Value Lists =========================================================
1079 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix
& rMatrix
)
1080 : mrMatrix( rMatrix
)
1084 XclExpCachedMatrix::~XclExpCachedMatrix()
1089 void XclExpCachedMatrix::GetDimensions( SCSIZE
& nCols
, SCSIZE
& nRows
) const
1091 mrMatrix
.GetDimensions( nCols
, nRows
);
1093 DBG_ASSERT( nCols
&& nRows
, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1094 DBG_ASSERT( nCols
<= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1097 sal_Size
XclExpCachedMatrix::GetSize() const
1099 SCSIZE nCols
, nRows
;
1101 GetDimensions( nCols
, nRows
);
1103 /* The returned size may be wrong if the matrix contains strings. The only
1104 effect is that the export stream has to update a wrong record size which is
1105 faster than to iterate through all cached values and calculate their sizes. */
1106 return 3 + 9 * (nCols
* nRows
);
1109 void XclExpCachedMatrix::Save( XclExpStream
& rStrm
) const
1111 SCSIZE nCols
, nRows
;
1113 GetDimensions( nCols
, nRows
);
1115 if( rStrm
.GetRoot().GetBiff() <= EXC_BIFF5
)
1116 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1117 rStrm
<< static_cast< sal_uInt8
>( nCols
) << static_cast< sal_uInt16
>( nRows
);
1119 // in BIFF8: columns and rows decreaed by 1
1120 rStrm
<< static_cast< sal_uInt8
>( nCols
- 1 ) << static_cast< sal_uInt16
>( nRows
- 1 );
1122 for( SCSIZE nRow
= 0; nRow
< nRows
; ++nRow
)
1124 for( SCSIZE nCol
= 0; nCol
< nCols
; ++nCol
)
1126 ScMatValType nMatValType
= SC_MATVAL_VALUE
;
1127 const ScMatrixValue
* pMatVal
= mrMatrix
.Get( nCol
, nRow
, nMatValType
);
1129 if( !pMatVal
|| SC_MATVAL_EMPTY
== nMatValType
)
1131 rStrm
.SetSliceSize( 9 );
1132 rStrm
<< EXC_CACHEDVAL_EMPTY
;
1133 rStrm
.WriteZeroBytes( 8 );
1135 else if( ScMatrix::IsNonValueType( nMatValType
) )
1137 XclExpString
aStr( pMatVal
->GetString(), EXC_STR_DEFAULT
);
1138 rStrm
.SetSliceSize( 6 );
1139 rStrm
<< EXC_CACHEDVAL_STRING
<< aStr
;
1141 else if( SC_MATVAL_BOOLEAN
== nMatValType
)
1143 sal_Int8 nBool
= pMatVal
->GetBoolean();
1144 rStrm
.SetSliceSize( 9 );
1145 rStrm
<< EXC_CACHEDVAL_BOOL
<< nBool
;
1146 rStrm
.WriteZeroBytes( 7 );
1148 else if( USHORT nScError
= pMatVal
->GetError() )
1150 sal_Int8
nError ( XclTools::GetXclErrorCode( nScError
) );
1151 rStrm
.SetSliceSize( 9 );
1152 rStrm
<< EXC_CACHEDVAL_ERROR
<< nError
;
1153 rStrm
.WriteZeroBytes( 7 );
1157 rStrm
.SetSliceSize( 9 );
1158 rStrm
<< EXC_CACHEDVAL_DOUBLE
<< pMatVal
->fVal
;
1164 // ============================================================================