diff options
Diffstat (limited to 'sc/source/filter/excel/xehelper.cxx')
-rw-r--r-- | sc/source/filter/excel/xehelper.cxx | 1144 |
1 files changed, 1144 insertions, 0 deletions
diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx new file mode 100644 index 000000000000..bce037bd05c1 --- /dev/null +++ b/sc/source/filter/excel/xehelper.cxx @@ -0,0 +1,1144 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <sfx2/objsh.hxx> +#include <vcl/font.hxx> +#include <tools/urlobj.hxx> +#include <svl/itemset.hxx> +#include <svtools/ctrltool.hxx> +#include <svx/svdotext.hxx> +#include <editeng/outlobj.hxx> +#include "scitems.hxx" +#include <editeng/fhgtitem.hxx> +#include <editeng/flstitem.hxx> +#include <editeng/colritem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/flditem.hxx> +#include <editeng/escpitem.hxx> +#include <editeng/svxfont.hxx> + +#define _SVSTDARR_USHORTS +#include <svl/svstdarr.hxx> +#include "document.hxx" +#include "docpool.hxx" +#include "cell.hxx" +#include "editutil.hxx" +#include "patattr.hxx" +#include "xestyle.hxx" +#include "fprogressbar.hxx" +#include "xltracer.hxx" +#include "xecontent.hxx" +#include "xelink.hxx" +#include "xehelper.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::i18n::XBreakIterator; + +// Export progress bar ======================================================== + +XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ), + mpSubProgress( 0 ), + mpSubRowCreate( 0 ), + mpSubRowFinal( 0 ), + mnSegRowFinal( SCF_INV_SEGMENT ), + mnRowCount( 0 ) +{ +} + +XclExpProgressBar::~XclExpProgressBar() +{ +} + +void XclExpProgressBar::Initialize() +{ + const ScDocument& rDoc = GetDoc(); + const XclExpTabInfo& rTabInfo = GetTabInfo(); + SCTAB nScTabCount = rTabInfo.GetScTabCount(); + + // *** segment: creation of ROW records *** ------------------------------- + + sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 ); + mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate ); + maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT ); + + for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab ) + { + if( rTabInfo.IsExportTab( nScTab ) ) + { + SCCOL nLastUsedScCol; + SCROW nLastUsedScRow; + rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow ); + sal_Size nSegSize = static_cast< sal_Size >( nLastUsedScRow + 1 ); + maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize ); + } + } + + // *** segment: writing all ROW records *** ------------------------------- + + mnSegRowFinal = mxProgress->AddSegment( 1000 ); + // sub progress bar and segment are created later in ActivateFinalRowsSegment() +} + +void XclExpProgressBar::IncRowRecordCount() +{ + ++mnRowCount; +} + +void XclExpProgressBar::ActivateCreateRowsSegment() +{ + DBG_ASSERT( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()), + "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" ); + sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ]; + DBG_ASSERT( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" ); + if( nSeg != SCF_INV_SEGMENT ) + { + mpSubProgress = mpSubRowCreate; + mpSubProgress->ActivateSegment( nSeg ); + } + else + mpSubProgress = 0; +} + +void XclExpProgressBar::ActivateFinalRowsSegment() +{ + if( !mpSubRowFinal && (mnRowCount > 0) ) + { + mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal ); + mpSubRowFinal->AddSegment( mnRowCount ); + } + mpSubProgress = mpSubRowFinal; + if( mpSubProgress ) + mpSubProgress->Activate(); +} + +void XclExpProgressBar::Progress() +{ + if( mpSubProgress && !mpSubProgress->IsFull() ) + mpSubProgress->Progress(); +} + +// Calc->Excel cell address/range conversion ================================== + +namespace { + +/** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */ +inline void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow ) +{ + rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol ); + rXclPos.mnRow = static_cast< sal_uInt16 >( nScRow ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) : + XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() ) +{ +} + +// cell address --------------------------------------------------------------- + +bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn ) +{ + // ScAddress::operator<=() doesn't do what we want here + bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col()); + bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row()); + bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab()); + + bool bValid = bValidCol && bValidRow && bValidTab; + if( !bValid && bWarn ) + { + mbColTrunc |= !bValidCol; + mbRowTrunc |= !bValidRow; + mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs + mrTracer.TraceInvalidAddress( rScPos, maMaxPos ); + } + return bValid; +} + +bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos, + const ScAddress& rScPos, bool bWarn ) +{ + bool bValid = CheckAddress( rScPos, bWarn ); + if( bValid ) + lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() ); + return bValid; +} + +XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn ) +{ + XclAddress aXclPos( ScAddress::UNINITIALIZED ); + if( !ConvertAddress( aXclPos, rScPos, bWarn ) ) + lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) ); + return aXclPos; +} + +// cell range ----------------------------------------------------------------- + +bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn ) +{ + return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn ); +} + +bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn ) +{ + rScRange.Justify(); + + // check start position + bool bValidStart = CheckAddress( rScRange.aStart, bWarn ); + if( bValidStart ) + { + // check & correct end position + ScAddress& rScEnd = rScRange.aEnd; + if( !CheckAddress( rScEnd, bWarn ) ) + { + rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) ); + rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) ); + rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) ); + } + } + + return bValidStart; +} + +bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange, + const ScRange& rScRange, bool bWarn ) +{ + // check start position + bool bValidStart = CheckAddress( rScRange.aStart, bWarn ); + if( bValidStart ) + { + lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() ); + + // check & correct end position + SCCOL nScCol2 = rScRange.aEnd.Col(); + SCROW nScRow2 = rScRange.aEnd.Row(); + if( !CheckAddress( rScRange.aEnd, bWarn ) ) + { + nScCol2 = ::std::min( nScCol2, maMaxPos.Col() ); + nScRow2 = ::std::min( nScRow2, maMaxPos.Row() ); + } + lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 ); + } + return bValidStart; +} + +//UNUSED2008-05 XclRange XclExpAddressConverter::CreateValidRange( const ScRange& rScRange, bool bWarn ) +//UNUSED2008-05 { +//UNUSED2008-05 return XclRange( +//UNUSED2008-05 CreateValidAddress( rScRange.aStart, bWarn ), +//UNUSED2008-05 CreateValidAddress( rScRange.aEnd, bWarn ) ); +//UNUSED2008-05 } + +// cell range list ------------------------------------------------------------ + +//UNUSED2008-05 bool XclExpAddressConverter::CheckRangeList( const ScRangeList& rScRanges, bool bWarn ) +//UNUSED2008-05 { +//UNUSED2008-05 for( ULONG nIdx = 0, nSize = rScRanges.Count(); nIdx < nSize; ++nIdx ) +//UNUSED2008-05 if( const ScRange* pScRange = rScRanges.GetObject( nIdx ) ) +//UNUSED2008-05 if( !CheckRange( *pScRange, bWarn ) ) +//UNUSED2008-05 return false; +//UNUSED2008-05 return true; +//UNUSED2008-05 } + +void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn ) +{ + ULONG nIdx = rScRanges.Count(); + while( nIdx ) + { + --nIdx; // backwards to keep nIdx valid + ScRange* pScRange = rScRanges.GetObject( nIdx ); + if( pScRange && !CheckRange( *pScRange, bWarn ) ) + delete rScRanges.Remove( nIdx ); + } +} + +void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges, + const ScRangeList& rScRanges, bool bWarn ) +{ + rXclRanges.clear(); + for( ULONG nPos = 0, nCount = rScRanges.Count(); nPos < nCount; ++nPos ) + { + if( const ScRange* pScRange = rScRanges.GetObject( nPos ) ) + { + XclRange aXclRange( ScAddress::UNINITIALIZED ); + if( ConvertRange( aXclRange, *pScRange, bWarn ) ) + rXclRanges.push_back( aXclRange ); + } + } +} + +// EditEngine->String conversion ============================================== + +namespace { + +String lclGetUrlRepresentation( const SvxURLField& rUrlField ) +{ + String aRepr( rUrlField.GetRepresentation() ); + // no representation -> use URL + return aRepr.Len() ? aRepr : rUrlField.GetURL(); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) : + XclExpRoot( rRoot ), + maScPos( rScPos ), + mbMultipleUrls( false ) +{ +} + +XclExpHyperlinkHelper::~XclExpHyperlinkHelper() +{ +} + +String XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField ) +{ + String aUrlRepr; + + if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7 + { + // there was/is already a HLINK record + mbMultipleUrls = mxLinkRec.is(); + + mxLinkRec.reset( new XclExpHyperlink( GetRoot(), rUrlField, maScPos ) ); + + if( const String* pRepr = mxLinkRec->GetRepr() ) + aUrlRepr = *pRepr; + + // add URL to note text + ScGlobal::AddToken( maUrlList, rUrlField.GetURL(), '\n' ); + } + + // no hyperlink representation from Excel HLINK record -> use it from text field + return aUrlRepr.Len() ? aUrlRepr : lclGetUrlRepresentation( rUrlField ); +} + +bool XclExpHyperlinkHelper::HasLinkRecord() const +{ + return !mbMultipleUrls && mxLinkRec.is(); +} + +XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord() +{ + if( HasLinkRecord() ) + return mxLinkRec; + return XclExpHyperlinkRef(); +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Creates a new formatted string from the passed unformatted string. + + Creates a Unicode string or a byte string, depending on the current BIFF + version contained in the passed XclExpRoot object. May create a formatted + string object, if the text contains different script types. + + @param pCellAttr + Cell attributes used for font formatting. + @param nFlags + Modifiers for string export. + @param nMaxLen + The maximum number of characters to store in this string. + @return + The new string object. + */ +XclExpStringRef lclCreateFormattedString( + const XclExpRoot& rRoot, const String& rText, const ScPatternAttr* pCellAttr, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + /* Create an empty Excel string object with correctly initialized BIFF mode, + because this function only uses Append() functions that require this. */ + XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + + // script type handling + Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator(); + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + // #i63255# get script type for leading weak characters + sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText ); + + // font buffer and cell item set + XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer(); + const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet(); + + // process all script portions + OUString aOUText( rText ); + sal_Int32 nPortionPos = 0; + sal_Int32 nTextLen = aOUText.getLength(); + while( nPortionPos < nTextLen ) + { + // get script type and end position of next script portion + sal_Int16 nScript = xBreakIt->getScriptType( aOUText, nPortionPos ); + sal_Int32 nPortionEnd = xBreakIt->endOfScript( aOUText, nPortionPos, nScript ); + + // reuse previous script for following weak portions + if( nScript == ApiScriptType::WEAK ) + nScript = nLastScript; + + // construct font from current text portion + SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) ); + + // Excel start position of this portion + sal_uInt16 nXclPortionStart = xString->Len(); + // add portion text to Excel string + XclExpStringHelper::AppendString( *xString, rRoot, aOUText.copy( nPortionPos, nPortionEnd - nPortionPos ) ); + if( nXclPortionStart < xString->Len() ) + { + // insert font into buffer + sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT ); + // insert font index into format run vector + xString->AppendFormat( nXclPortionStart, nFontIdx ); + } + + // go to next script portion + nLastScript = nScript; + nPortionPos = nPortionEnd; + } + + return xString; +} + +/** Creates a new formatted string from an edit engine text object. + + Creates a Unicode string or a byte string, depending on the current BIFF + version contained in the passed XclExpRoot object. + + @param rEE + The edit engine in use. The text object must already be set. + @param nFlags + Modifiers for string export. + @param nMaxLen + The maximum number of characters to store in this string. + @return + The new string object. + */ +XclExpStringRef lclCreateFormattedString( + const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + /* Create an empty Excel string object with correctly initialized BIFF mode, + because this function only uses Append() functions that require this. */ + XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + + // font buffer and helper item set for edit engine -> Calc item conversion + XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer(); + SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END ); + + // script type handling + Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator(); + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + // #i63255# get script type for leading weak characters + sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() ); + + // process all paragraphs + sal_uInt16 nParaCount = rEE.GetParagraphCount(); + for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara ) + { + ESelection aSel( nPara, 0 ); + String aParaText( rEE.GetText( nPara ) ); + + SvUShorts aPosList; + rEE.GetPortions( nPara, aPosList ); + + // process all portions in the paragraph + sal_uInt16 nPosCount = aPosList.Count(); + for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos ) + { + aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) ); + String aXclPortionText( aParaText, aSel.nStartPos, aSel.nEndPos - aSel.nStartPos ); + + aItemSet.ClearItem(); + SfxItemSet aEditSet( rEE.GetAttribs( aSel ) ); + ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet ); + + // get escapement value + short nEsc = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ).GetEsc(); + + // process text fields + bool bIsHyperlink = false; + if( aSel.nStartPos + 1 == aSel.nEndPos ) + { + // test if the character is a text field + const SfxPoolItem* pItem; + if( aEditSet.GetItemState( EE_FEATURE_FIELD, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField(); + if( const SvxURLField* pUrlField = PTR_CAST( SvxURLField, pField ) ) + { + // convert URL field to string representation + aXclPortionText = pLinkHelper ? + pLinkHelper->ProcessUrlField( *pUrlField ) : + lclGetUrlRepresentation( *pUrlField ); + bIsHyperlink = true; + } + else + { + DBG_ERRORFILE( "lclCreateFormattedString - unknown text field" ); + aXclPortionText.Erase(); + } + } + } + + // Excel start position of this portion + sal_uInt16 nXclPortionStart = xString->Len(); + // add portion text to Excel string + XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText ); + if( (nXclPortionStart < xString->Len()) || (aParaText.Len() == 0) ) + { + /* Construct font from current edit engine text portion. Edit engine + creates different portions for different script types, no need to loop. */ + sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 ); + if( nScript == ApiScriptType::WEAK ) + nScript = nLastScript; + SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) ); + nLastScript = nScript; + + // add escapement + aFont.SetEscapement( nEsc ); + // modify automatic font color for hyperlinks + if( bIsHyperlink && (GETITEM( aItemSet, SvxColorItem, ATTR_FONT_COLOR ).GetValue().GetColor() == COL_AUTO) ) + aFont.SetColor( Color( COL_LIGHTBLUE ) ); + + // insert font into buffer + sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT ); + // insert font index into format run vector + xString->AppendFormat( nXclPortionStart, nFontIdx ); + } + + aSel.nStartPos = aSel.nEndPos; + } + + // add trailing newline (important for correct character index calculation) + if( nPara + 1 < nParaCount ) + XclExpStringHelper::AppendChar( *xString, rRoot, '\n' ); + } + + return xString; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, const String& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString( new XclExpString ); + if( rRoot.GetBiff() == EXC_BIFF8 ) + xString->Assign( rString, nFlags, nMaxLen ); + else + xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen ); + return xString; +} + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + AppendChar( *xString, rRoot, cChar ); + return xString; +} + +void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const String& rString ) +{ + if( rRoot.GetBiff() == EXC_BIFF8 ) + rXclString.Append( rString ); + else + rXclString.AppendByte( rString, rRoot.GetTextEncoding() ); +} + +void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar ) +{ + if( rRoot.GetBiff() == EXC_BIFF8 ) + rXclString.Append( cChar ); + else + rXclString.AppendByte( cChar, rRoot.GetTextEncoding() ); +} + +XclExpStringRef XclExpStringHelper::CreateCellString( + const XclExpRoot& rRoot, const ScStringCell& rStringCell, const ScPatternAttr* pCellAttr, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + String aCellText; + rStringCell.GetString( aCellText ); + return lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen ); +} + +XclExpStringRef XclExpStringHelper::CreateCellString( + const XclExpRoot& rRoot, const ScEditCell& rEditCell, const ScPatternAttr* pCellAttr, + XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString; + if( const EditTextObject* pEditObj = rEditCell.GetData() ) + { + // formatted cell + ScEditEngineDefaulter& rEE = rRoot.GetEditEngine(); + BOOL bOldUpdateMode = rEE.GetUpdateMode(); + rEE.SetUpdateMode( TRUE ); + // default items + const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet(); + SfxItemSet* pEEItemSet = new SfxItemSet( rEE.GetEmptyItemSet() ); + ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet ); + rEE.SetDefaults( pEEItemSet ); // edit engine takes ownership + // create the string + rEE.SetText( *pEditObj ); + xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen ); + rEE.SetUpdateMode( bOldUpdateMode ); + } + else + { + // unformatted cell + String aCellText; + rEditCell.GetString( aCellText ); + xString = lclCreateFormattedString( rRoot, aCellText, pCellAttr, nFlags, nMaxLen ); + } + return xString; +} + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, const SdrTextObj& rTextObj, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString; + if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() ) + { + EditEngine& rEE = rRoot.GetDrawEditEngine(); + BOOL bOldUpdateMode = rEE.GetUpdateMode(); + rEE.SetUpdateMode( TRUE ); + // create the string + rEE.SetText( pParaObj->GetTextObject() ); + xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen ); + rEE.SetUpdateMode( bOldUpdateMode ); + // limit formats - TODO: BIFF dependent + if( !xString->IsEmpty() ) + { + xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 ); + xString->AppendTrailingFormat( EXC_FONT_APP ); + } + } + else + { + DBG_ERRORFILE( "XclExpStringHelper::CreateString - textbox without para object" ); + // create BIFF dependent empty Excel string + xString = CreateString( rRoot, EMPTY_STRING, nFlags, nMaxLen ); + } + return xString; +} + +XclExpStringRef XclExpStringHelper::CreateString( + const XclExpRoot& rRoot, const EditTextObject& rEditObj, + XclStrFlags nFlags, sal_uInt16 nMaxLen ) +{ + XclExpStringRef xString; + EditEngine& rEE = rRoot.GetDrawEditEngine(); + BOOL bOldUpdateMode = rEE.GetUpdateMode(); + rEE.SetUpdateMode( TRUE ); + rEE.SetText( rEditObj ); + xString = lclCreateFormattedString( rRoot, rEE, 0, nFlags, nMaxLen ); + rEE.SetUpdateMode( bOldUpdateMode ); + // limit formats - TODO: BIFF dependent + if( !xString->IsEmpty() ) + { + xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 ); + xString->AppendTrailingFormat( EXC_FONT_APP ); + } + return xString; +} + +sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const String& rString ) +{ + namespace ApiScriptType = ::com::sun::star::i18n::ScriptType; + Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator(); + OUString aOUString( rString ); + sal_Int32 nStrPos = 0; + sal_Int32 nStrLen = aOUString.getLength(); + sal_Int16 nScript = ApiScriptType::WEAK; + while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) ) + { + nScript = xBreakIt->getScriptType( aOUString, nStrPos ); + nStrPos = xBreakIt->endOfScript( aOUString, nStrPos, nScript ); + } + return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript; +} + +// Header/footer conversion =================================================== + +XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) : + XclExpRoot( rRoot ), + mrEE( rRoot.GetHFEditEngine() ), + mnTotalHeight( 0 ) +{ +} + +void XclExpHFConverter::GenerateString( + const EditTextObject* pLeftObj, + const EditTextObject* pCenterObj, + const EditTextObject* pRightObj ) +{ + maHFString.Erase(); + mnTotalHeight = 0; + AppendPortion( pLeftObj, 'L' ); + AppendPortion( pCenterObj, 'C' ); + AppendPortion( pRightObj, 'R' ); +} + +void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode ) +{ + if( !pTextObj ) return; + + String aText; + sal_Int32 nHeight = 0; + SfxItemSet aItemSet( *GetDoc().GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END ); + + // edit engine + BOOL bOldUpdateMode = mrEE.GetUpdateMode(); + mrEE.SetUpdateMode( TRUE ); + mrEE.SetText( *pTextObj ); + + // font information + XclFontData aFontData, aNewData; + if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) ) + { + aFontData = pFirstFont->GetFontData(); + (aFontData.mnHeight += 10) /= 20; // using pt here, not twips + } + else + aFontData.mnHeight = 10; + + const FontList* pFontList = 0; + if( SfxObjectShell* pDocShell = GetDocShell() ) + { + if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >( + pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) ) + pFontList = pInfoItem->GetFontList(); + } + + sal_uInt16 nParaCount = mrEE.GetParagraphCount(); + for( sal_uInt16 nPara = 0; nPara < nParaCount; ++nPara ) + { + ESelection aSel( nPara, 0 ); + String aParaText; + sal_Int32 nParaHeight = 0; + SvUShorts aPosList; + mrEE.GetPortions( nPara, aPosList ); + + sal_uInt16 nPosCount = aPosList.Count(); + for( sal_uInt16 nPos = 0; nPos < nPosCount; ++nPos ) + { + aSel.nEndPos = static_cast< xub_StrLen >( aPosList.GetObject( nPos ) ); + if( aSel.nStartPos < aSel.nEndPos ) + { + +// --- font attributes --- + + Font aFont; + aItemSet.ClearItem(); + SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) ); + ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet ); + ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW ); + + // font name and style + aNewData.maName = XclTools::GetXclFontName( aFont.GetName() ); + aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL; + aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE); + bool bNewFont = !(aFontData.maName == aNewData.maName); + bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) || + (aFontData.mbItalic != aNewData.mbItalic); + if( bNewFont || (bNewStyle && pFontList) ) + { + aParaText.AppendAscii( "&\"" ).Append( aNewData.maName ); + if( pFontList ) + { + FontInfo aFontInfo( pFontList->Get( + aNewData.maName, + (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL, + aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) ); + aNewData.maStyle = pFontList->GetStyleName( aFontInfo ); + if( aNewData.maStyle.Len() ) + aParaText.Append( ',' ).Append( aNewData.maStyle ); + } + aParaText.Append( '"' ); + } + + // height + // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm + // -> get it directly from edit engine item set + aNewData.mnHeight = ulimit_cast< sal_uInt16 >( GETITEM( aEditSet, SvxFontHeightItem, EE_CHAR_FONTHEIGHT ).GetHeight() ); + (aNewData.mnHeight += 10) /= 20; + bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight); + if( bFontHtChanged ) + aParaText.Append( '&' ).Append( String::CreateFromInt32( aNewData.mnHeight ) ); + // update maximum paragraph height, convert to twips + nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 ); + + // underline + aNewData.mnUnderline = EXC_FONTUNDERL_NONE; + switch( aFont.GetUnderline() ) + { + case UNDERLINE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break; + case UNDERLINE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break; + case UNDERLINE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break; + default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; + } + if( aFontData.mnUnderline != aNewData.mnUnderline ) + { + sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ? + aFontData.mnUnderline : aNewData.mnUnderline; + aParaText.AppendAscii( (nTmpUnderl == EXC_FONTUNDERL_SINGLE) ? "&U" : "&E" ); + } + + // strikeout + aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE); + if( aFontData.mbStrikeout != aNewData.mbStrikeout ) + aParaText.AppendAscii( "&S" ); + + // super/sub script + const SvxEscapementItem& rEscapeItem = GETITEM( aEditSet, SvxEscapementItem, EE_CHAR_ESCAPEMENT ); + aNewData.SetScEscapement( rEscapeItem.GetEsc() ); + if( aFontData.mnEscapem != aNewData.mnEscapem ) + { + switch(aNewData.mnEscapem) + { + // close the previous super/sub script. + case EXC_FONTESC_NONE: aParaText.AppendAscii( (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? "&X" : "&Y" ); break; + case EXC_FONTESC_SUPER: aParaText.AppendAscii( "&X" ); break; + case EXC_FONTESC_SUB: aParaText.AppendAscii( "&Y" ); break; + default: break; + } + } + + aFontData = aNewData; + +// --- text content or text fields --- + + const SfxPoolItem* pItem; + if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters + (aEditSet.GetItemState( EE_FEATURE_FIELD, sal_False, &pItem ) == SFX_ITEM_SET) ) + { + if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() ) + { + if( pFieldData->ISA( SvxPageField ) ) + aParaText.AppendAscii( "&P" ); + else if( pFieldData->ISA( SvxPagesField ) ) + aParaText.AppendAscii( "&N" ); + else if( pFieldData->ISA( SvxDateField ) ) + aParaText.AppendAscii( "&D" ); + else if( pFieldData->ISA( SvxTimeField ) || pFieldData->ISA( SvxExtTimeField ) ) + aParaText.AppendAscii( "&T" ); + else if( pFieldData->ISA( SvxTableField ) ) + aParaText.AppendAscii( "&A" ); + else if( pFieldData->ISA( SvxFileField ) ) // title -> file name + aParaText.AppendAscii( "&F" ); + else if( const SvxExtFileField* pFileField = PTR_CAST( SvxExtFileField, pFieldData ) ) + { + switch( pFileField->GetFormat() ) + { + case SVXFILEFORMAT_NAME_EXT: + case SVXFILEFORMAT_NAME: + aParaText.AppendAscii( "&F" ); + break; + case SVXFILEFORMAT_PATH: + aParaText.AppendAscii( "&Z" ); + break; + case SVXFILEFORMAT_FULLPATH: + aParaText.AppendAscii( "&Z&F" ); + break; + default: + DBG_ERRORFILE( "XclExpHFConverter::AppendPortion - unknown file field" ); + } + } + } + } + else + { + String aPortionText( mrEE.GetText( aSel ) ); + aPortionText.SearchAndReplaceAll( String( '&' ), String( RTL_CONSTASCII_USTRINGPARAM( "&&" ) ) ); + // #i17440# space between font height and numbers in text + if( bFontHtChanged && aParaText.Len() && aPortionText.Len() ) + { + sal_Unicode cLast = aParaText.GetChar( aParaText.Len() - 1 ); + sal_Unicode cFirst = aPortionText.GetChar( 0 ); + if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') ) + aParaText.Append( ' ' ); + } + aParaText.Append( aPortionText ); + } + } + + aSel.nStartPos = aSel.nEndPos; + } + + ScGlobal::AddToken( aText, aParaText, '\n' ); + if( nParaHeight == 0 ) + nParaHeight = aFontData.mnHeight * 20; // points -> twips + nHeight += nParaHeight; + } + + mrEE.SetUpdateMode( bOldUpdateMode ); + + if( aText.Len() ) + { + maHFString.Append( '&' ).Append( cPortionCode ).Append( aText ); + mnTotalHeight = ::std::max( mnTotalHeight, nHeight ); + } +} + +// URL conversion ============================================================= + +namespace { + +/** Converts the file URL passed in rUrl to a URL in DOS notation (local or UNC). + @param rUrl (in/out-param) In: URL to convert; Out: Converted URL in DOS notation. + @param rBasePath Base path for relative URLs. + @param bSaveRelUrl Converts to a relative URL, using rBasePath. + @return True = Conversion successful, rUrl contains converted file URL. */ +bool lclConvertToDos( String& rUrl, const String& rBasePath, bool bSaveRelUrl ) +{ + String aDosUrl( INetURLObject( rUrl ).getFSysPath( INetURLObject::FSYS_DOS ) ); + bool bRet = (aDosUrl.Len() > 0); + if( bRet && bSaveRelUrl ) + { + // try to convert to relative path + String aDosBase( INetURLObject( rBasePath ).getFSysPath( INetURLObject::FSYS_DOS ) ); + if( aDosBase.Len() ) + { + xub_StrLen nPos; + + // --- 1st step: delete equal subdirectories --- + + // special handling for UNC + xub_StrLen nStartSearch = aDosBase.EqualsAscii( "\\\\", 0, 2 ) ? 2 : 0; + bool bEqualBase = false; + bool bLoop = true; + while( bLoop && ((nPos = aDosBase.Search( '\\', nStartSearch )) != STRING_NOTFOUND) ) + { + bLoop = (TRUE == aDosBase.Equals( aDosUrl, 0, nPos + 1 )); + if( bLoop ) + { + aDosBase.Erase( 0, nPos + 1 ); + aDosUrl.Erase( 0, nPos + 1 ); + nStartSearch = 0; + bEqualBase = true; + } + } + + // --- 2nd step: add parent directory levels --- + + if( bEqualBase ) + { + while( (nPos = aDosBase.Search( '\\' )) != STRING_NOTFOUND ) + { + aDosBase.Erase( 0, nPos + 1 ); + aDosUrl.InsertAscii( "..\\", 0 ); + } + } + } + rUrl = aDosUrl; + } + return bRet; +} + +/** Encodes special parts of the URL, i.e. directory separators and volume names. + @param pTableName Pointer to a table name to be encoded in this URL, or 0. */ +void lclEncodeDosUrl( XclBiff eBiff, String& rUrl, const String* pTableName = 0 ) +{ + if( rUrl.Len() ) + { + String aOldUrl( rUrl ); + rUrl = EXC_URLSTART_ENCODED; + + if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( "\\\\", 0, 2 ) ) + { + // UNC + rUrl.Append( EXC_URL_DOSDRIVE ).Append( '@' ); + aOldUrl.Erase( 0, 2 ); + } + else if( (aOldUrl.Len() > 2) && aOldUrl.EqualsAscii( ":\\", 1, 2 ) ) + { + // drive letter + rUrl.Append( EXC_URL_DOSDRIVE ).Append( aOldUrl.GetChar( 0 ) ); + aOldUrl.Erase( 0, 3 ); + } + + // directories + xub_StrLen nPos; + while( (nPos = aOldUrl.Search( '\\' )) != STRING_NOTFOUND ) + { + if( aOldUrl.EqualsAscii( "..", 0, 2 ) ) + rUrl.Append( EXC_URL_PARENTDIR ); // parent dir + else + rUrl.Append( aOldUrl.GetBuffer(), nPos ).Append( EXC_URL_SUBDIR ); + aOldUrl.Erase( 0, nPos + 1 ); + } + + // file name + if( pTableName ) // enclose file name in brackets if table name follows + rUrl.Append( '[' ).Append( aOldUrl ).Append( ']' ); + else + rUrl.Append( aOldUrl ); + } + else // empty URL -> self reference + { + switch( eBiff ) + { + case EXC_BIFF5: + rUrl = pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF; + break; + case EXC_BIFF8: + DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" ); + rUrl = EXC_URLSTART_SELF; + break; + default: + DBG_ERROR_BIFF(); + } + } + + // table name + if( pTableName ) + rUrl.Append( *pTableName ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +String XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const String& rAbsUrl, const String* pTableName ) +{ + String aDosUrl( rAbsUrl ); + if( !aDosUrl.Len() || lclConvertToDos( aDosUrl, rRoot.GetBasePath(), rRoot.IsRelUrl() ) ) + lclEncodeDosUrl( rRoot.GetBiff(), aDosUrl, pTableName ); + return aDosUrl; +} + +String XclExpUrlHelper::EncodeDde( const String& rApplic, const String rTopic ) +{ + String aDde( rApplic ); + aDde.Append( EXC_DDE_DELIM ).Append( rTopic ); + return aDde; +} + +// Cached Value Lists ========================================================= + +XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix ) + : mrMatrix( rMatrix ) +{ + mrMatrix.IncRef(); +} +XclExpCachedMatrix::~XclExpCachedMatrix() +{ + mrMatrix.DecRef(); +} + +void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const +{ + mrMatrix.GetDimensions( nCols, nRows ); + + DBG_ASSERT( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" ); + DBG_ASSERT( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" ); +} + +sal_Size XclExpCachedMatrix::GetSize() const +{ + SCSIZE nCols, nRows; + + GetDimensions( nCols, nRows ); + + /* The returned size may be wrong if the matrix contains strings. The only + effect is that the export stream has to update a wrong record size which is + faster than to iterate through all cached values and calculate their sizes. */ + return 3 + 9 * (nCols * nRows); +} + +void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const +{ + SCSIZE nCols, nRows; + + GetDimensions( nCols, nRows ); + + if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 ) + // in BIFF2-BIFF7: 256 columns represented by 0 columns + rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows ); + else + // in BIFF8: columns and rows decreaed by 1 + rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 ); + + for( SCSIZE nRow = 0; nRow < nRows; ++nRow ) + { + for( SCSIZE nCol = 0; nCol < nCols; ++nCol ) + { + ScMatValType nMatValType = SC_MATVAL_VALUE; + const ScMatrixValue* pMatVal = mrMatrix.Get( nCol, nRow, nMatValType ); + + if( !pMatVal || SC_MATVAL_EMPTY == nMatValType ) + { + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_EMPTY; + rStrm.WriteZeroBytes( 8 ); + } + else if( ScMatrix::IsNonValueType( nMatValType ) ) + { + XclExpString aStr( pMatVal->GetString(), EXC_STR_DEFAULT ); + rStrm.SetSliceSize( 6 ); + rStrm << EXC_CACHEDVAL_STRING << aStr; + } + else if( SC_MATVAL_BOOLEAN == nMatValType ) + { + sal_Int8 nBool = pMatVal->GetBoolean(); + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_BOOL << nBool; + rStrm.WriteZeroBytes( 7 ); + } + else if( USHORT nScError = pMatVal->GetError() ) + { + sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) ); + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_ERROR << nError; + rStrm.WriteZeroBytes( 7 ); + } + else + { + rStrm.SetSliceSize( 9 ); + rStrm << EXC_CACHEDVAL_DOUBLE << pMatVal->fVal; + } + } + } +} + +// ============================================================================ + |