blob: 809e87bf1b3153a5046ece505788782b0e9f853d [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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( sal_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 )
{
sal_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( sal_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, sal_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();
sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
rEE.SetUpdateMode( sal_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();
sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
rEE.SetUpdateMode( sal_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();
sal_Bool bOldUpdateMode = rEE.GetUpdateMode();
rEE.SetUpdateMode( sal_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
sal_Bool bOldUpdateMode = mrEE.GetUpdateMode();
mrEE.SetUpdateMode( sal_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 = (sal_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( sal_uInt16 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;
}
}
}
}
// ============================================================================