diff options
author | Rüdiger Timm <rt@openoffice.org> | 2008-01-17 07:06:10 +0000 |
---|---|---|
committer | Rüdiger Timm <rt@openoffice.org> | 2008-01-17 07:06:10 +0000 |
commit | 3381981e76873304b171f7df900561dac681d2af (patch) | |
tree | f496d5a2006e8719b5783d5a8966a05858ed3014 /oox/source/xls | |
parent | 90e7bde2a1f3dd8c81e947578f14f40059961740 (diff) |
#i10000# Bring module to HEAD.
Diffstat (limited to 'oox/source/xls')
49 files changed, 28262 insertions, 0 deletions
diff --git a/oox/source/xls/addressconverter.cxx b/oox/source/xls/addressconverter.cxx new file mode 100644 index 000000000000..9d2a781cfdc2 --- /dev/null +++ b/oox/source/xls/addressconverter.cxx @@ -0,0 +1,772 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: addressconverter.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:07 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/addressconverter.hxx" +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <rtl/strbuf.hxx> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include "oox/helper/recordinputstream.hxx" +#include "oox/core/filterbase.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/biffoutputstream.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OStringBuffer; +using ::rtl::OUStringToOString; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::sheet::XCellRangeAddressable; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +//! TODO: this limit may be changed +const sal_Int16 API_MAXTAB = 255; + +const sal_Int32 OOX_MAXCOL = static_cast< sal_Int32 >( (1 << 14) - 1 ); +const sal_Int32 OOX_MAXROW = static_cast< sal_Int32 >( (1 << 20) - 1 ); +const sal_Int16 OOX_MAXTAB = static_cast< sal_Int16 >( (1 << 15) - 1 ); + +const sal_Int32 BIFF2_MAXCOL = 255; +const sal_Int32 BIFF2_MAXROW = 16383; +const sal_Int16 BIFF2_MAXTAB = 0; + +const sal_Int32 BIFF3_MAXCOL = BIFF2_MAXCOL; +const sal_Int32 BIFF3_MAXROW = BIFF2_MAXROW; +const sal_Int16 BIFF3_MAXTAB = BIFF2_MAXTAB; + +const sal_Int32 BIFF4_MAXCOL = BIFF3_MAXCOL; +const sal_Int32 BIFF4_MAXROW = BIFF3_MAXROW; +const sal_Int16 BIFF4_MAXTAB = 32767; + +const sal_Int32 BIFF5_MAXCOL = BIFF4_MAXCOL; +const sal_Int32 BIFF5_MAXROW = BIFF4_MAXROW; +const sal_Int16 BIFF5_MAXTAB = BIFF4_MAXTAB; + +const sal_Int32 BIFF8_MAXCOL = BIFF5_MAXCOL; +const sal_Int32 BIFF8_MAXROW = 65535; +const sal_Int16 BIFF8_MAXTAB = BIFF5_MAXTAB; + +const sal_Unicode BIFF_URL_DRIVE = '\x01'; /// DOS drive letter or UNC path. +const sal_Unicode BIFF_URL_ROOT = '\x02'; /// Root directory of current drive. +const sal_Unicode BIFF_URL_SUBDIR = '\x03'; /// Subdirectory delimiter. +const sal_Unicode BIFF_URL_PARENT = '\x04'; /// Parent directory. +const sal_Unicode BIFF_URL_RAW = '\x05'; /// Unencoded URL. +const sal_Unicode BIFF_URL_INSTALL = '\x06'; /// Application installation directory. +const sal_Unicode BIFF_URL_INSTALL2 = '\x07'; /// Alternative application installation directory. +const sal_Unicode BIFF_URL_ADDIN = '\x08'; /// Add-in installation directory. +const sal_Unicode BIFF4_URL_SHEET = '\x09'; /// BIFF4 internal sheet. +const sal_Unicode BIFF_URL_UNC = '@'; /// UNC path root. + + +inline sal_uInt16 lclGetBiffAddressSize( bool bCol16Bit, bool bRow32Bit ) +{ + return (bCol16Bit ? 2 : 1) + (bRow32Bit ? 4 : 2); +} + +inline sal_uInt16 lclGetBiffRangeSize( bool bCol16Bit, bool bRow32Bit ) +{ + return 2 * lclGetBiffAddressSize( bCol16Bit, bRow32Bit ); +} + +} // namespace + +// ============================================================================ +// ============================================================================ + +CellAddress ApiCellRangeList::getBaseAddress() const +{ + if( empty() ) + return CellAddress(); + return CellAddress( front().Sheet, front().StartColumn, front().StartRow ); +} + +// ============================================================================ + +void BinAddress::read( RecordInputStream& rStrm ) +{ + rStrm >> mnRow >> mnCol; +} + +void BinAddress::read( BiffInputStream& rStrm, bool bCol16Bit, bool bRow32Bit ) +{ + mnRow = bRow32Bit ? rStrm.readInt32() : rStrm.readuInt16(); + mnCol = bCol16Bit ? rStrm.readuInt16() : rStrm.readuInt8(); +} + +void BinAddress::write( BiffOutputStream& rStrm, bool bCol16Bit, bool bRow32Bit ) const +{ + if( bRow32Bit ) + rStrm << mnRow; + else + rStrm << static_cast< sal_uInt16 >( mnRow ); + if( bCol16Bit ) + rStrm << static_cast< sal_uInt16 >( mnCol ); + else + rStrm << static_cast< sal_uInt8 >( mnCol ); +} + +// ============================================================================ + +bool BinRange::contains( const BinAddress& rAddr ) const +{ + return (maFirst.mnCol <= rAddr.mnCol) && (rAddr.mnCol <= maLast.mnCol) && + (maFirst.mnRow <= rAddr.mnRow) && (rAddr.mnRow <= maLast.mnRow); +} + +void BinRange::read( RecordInputStream& rStrm ) +{ + rStrm >> maFirst.mnRow >> maLast.mnRow >> maFirst.mnCol >> maLast.mnCol; +} + +void BinRange::read( BiffInputStream& rStrm, bool bCol16Bit, bool bRow32Bit ) +{ + maFirst.mnRow = bRow32Bit ? rStrm.readInt32() : rStrm.readuInt16(); + maLast.mnRow = bRow32Bit ? rStrm.readInt32() : rStrm.readuInt16(); + maFirst.mnCol = bCol16Bit ? rStrm.readuInt16() : rStrm.readuInt8(); + maLast.mnCol = bCol16Bit ? rStrm.readuInt16() : rStrm.readuInt8(); +} + +void BinRange::write( BiffOutputStream& rStrm, bool bCol16Bit, bool bRow32Bit ) const +{ + if( bRow32Bit ) + rStrm << maFirst.mnRow << maLast.mnRow; + else + rStrm << static_cast< sal_uInt16 >( maFirst.mnRow ) << static_cast< sal_uInt16 >( maLast.mnRow ); + if( bCol16Bit ) + rStrm << static_cast< sal_uInt16 >( maFirst.mnCol ) << static_cast< sal_uInt16 >( maLast.mnCol ); + else + rStrm << static_cast< sal_uInt8 >( maFirst.mnCol ) << static_cast< sal_uInt8 >( maLast.mnCol ); +} + +// ============================================================================ + +BinRange BinRangeList::getEnclosingRange() const +{ + BinRange aRange; + if( !empty() ) + { + const_iterator aIt = begin(), aEnd = end(); + aRange = *aIt; + for( ++aIt; aIt != aEnd; ++aIt ) + { + aRange.maFirst.mnCol = ::std::min( aRange.maFirst.mnCol, aIt->maFirst.mnCol ); + aRange.maFirst.mnRow = ::std::min( aRange.maFirst.mnRow, aIt->maFirst.mnRow ); + aRange.maLast.mnCol = ::std::max( aRange.maLast.mnCol, aIt->maLast.mnCol ); + aRange.maLast.mnRow = ::std::max( aRange.maLast.mnRow, aIt->maLast.mnRow ); + } + } + return aRange; +} + +void BinRangeList::read( RecordInputStream& rStrm ) +{ + sal_Int32 nCount = rStrm.readInt32(); + resize( getLimitedValue< size_t, sal_Int32 >( nCount, 0, rStrm.getRecLeft() / 16 ) ); + for( iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + aIt->read( rStrm ); +} + +void BinRangeList::read( BiffInputStream& rStrm, bool bCol16Bit, bool bRow32Bit ) +{ + sal_uInt16 nCount = rStrm.readuInt16(); + resize( getLimitedValue< size_t, sal_uInt32 >( nCount, 0, rStrm.getRecLeft() / lclGetBiffRangeSize( bCol16Bit, bRow32Bit ) ) ); + for( iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + aIt->read( rStrm, bCol16Bit, bRow32Bit ); +} + +void BinRangeList::write( BiffOutputStream& rStrm, bool bCol16Bit, bool bRow32Bit ) const +{ + writeSubList( rStrm, 0, size(), bCol16Bit, bRow32Bit ); +} + +void BinRangeList::writeSubList( BiffOutputStream& rStrm, size_t nBegin, size_t nCount, bool bCol16Bit, bool bRow32Bit ) const +{ + OSL_ENSURE( nBegin <= size(), "BiffRangeList::writeSubList - invalid start position" ); + size_t nEnd = ::std::min< size_t >( nBegin + nCount, size() ); + sal_uInt16 nBiffCount = getLimitedValue< sal_uInt16, size_t >( nEnd - nBegin, 0, SAL_MAX_UINT16 ); + rStrm << nBiffCount; + rStrm.setPortionSize( lclGetBiffRangeSize( bCol16Bit, bRow32Bit ) ); + for( const_iterator aIt = begin() + nBegin, aEnd = begin() + nEnd; aIt != aEnd; ++aIt ) + aIt->write( rStrm, bCol16Bit, bRow32Bit ); +} + +// ============================================================================ +// ============================================================================ + +AddressConverter::AddressConverter( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mcUrlThisWorkbook( 0 ), + mcUrlExternal( 0 ), + mcUrlThisSheet( 0 ), + mcUrlInternal( 0 ), + mbColOverflow( false ), + mbRowOverflow( false ), + mbTabOverflow( false ) +{ + switch( getFilterType() ) + { + case FILTER_OOX: + initializeMaxPos( OOX_MAXTAB, OOX_MAXCOL, OOX_MAXROW ); + break; + case FILTER_BIFF: switch( getBiff() ) + { + case BIFF2: + initializeMaxPos( BIFF2_MAXTAB, BIFF2_MAXCOL, BIFF2_MAXROW ); + initializeEncodedUrl( '\x00', '\x01', '\x02', '\x00' ); + break; + case BIFF3: + initializeMaxPos( BIFF3_MAXTAB, BIFF3_MAXCOL, BIFF3_MAXROW ); + initializeEncodedUrl( '\x00', '\x01', '\x02', '\x00' ); + break; + case BIFF4: + initializeMaxPos( BIFF4_MAXTAB, BIFF4_MAXCOL, BIFF4_MAXROW ); + initializeEncodedUrl( '\x00', '\x01', '\x02', '\x00' ); + break; + case BIFF5: + initializeMaxPos( BIFF5_MAXTAB, BIFF5_MAXCOL, BIFF5_MAXROW ); + initializeEncodedUrl( '\x04', '\x01', '\x02', '\x03' ); + break; + case BIFF8: + initializeMaxPos( BIFF8_MAXTAB, BIFF8_MAXCOL, BIFF8_MAXROW ); + initializeEncodedUrl( '\x04', '\x01', '\x00', '\x02' ); + break; + case BIFF_UNKNOWN: break; + } + break; + case FILTER_UNKNOWN: break; + } +} + +// ---------------------------------------------------------------------------- + +bool AddressConverter::parseOoxAddress2d( + sal_Int32& ornColumn, sal_Int32& ornRow, + const OUString& rString, sal_Int32 nStart, sal_Int32 nLength ) +{ + ornColumn = ornRow = 0; + if( (nStart < 0) || (nStart >= rString.getLength()) || (nLength < 2) ) + return false; + + const sal_Unicode* pcChar = rString.getStr() + nStart; + const sal_Unicode* pcEndChar = pcChar + ::std::min( nLength, rString.getLength() - nStart ); + + enum { STATE_COL, STATE_ROW } eState = STATE_COL; + while( pcChar < pcEndChar ) + { + sal_Unicode cChar = *pcChar; + switch( eState ) + { + case STATE_COL: + { + if( ('a' <= cChar) && (cChar <= 'z') ) + (cChar -= 'a') += 'A'; + if( ('A' <= cChar) && (cChar <= 'Z') ) + { + /* Return, if 1-based column index is already 6 characters + long (12356631 is column index for column AAAAAA). */ + if( ornColumn >= 12356631 ) + return false; + (ornColumn *= 26) += (cChar - 'A' + 1); + } + else if( ornColumn > 0 ) + { + --pcChar; + eState = STATE_ROW; + } + else + return false; + } + break; + + case STATE_ROW: + { + if( ('0' <= cChar) && (cChar <= '9') ) + { + // return, if 1-based row is already 9 digits long + if( ornRow >= 100000000 ) + return false; + (ornRow *= 10) += (cChar - '0'); + } + else + return false; + } + break; + } + ++pcChar; + } + + --ornColumn; + --ornRow; + return (ornColumn >= 0) && (ornRow >= 0); +} + +bool AddressConverter::parseOoxRange2d( + sal_Int32& ornStartColumn, sal_Int32& ornStartRow, + sal_Int32& ornEndColumn, sal_Int32& ornEndRow, + const OUString& rString, sal_Int32 nStart, sal_Int32 nLength ) +{ + ornStartColumn = ornStartRow = ornEndColumn = ornEndRow = 0; + if( (nStart < 0) || (nStart >= rString.getLength()) || (nLength < 2) ) + return false; + + sal_Int32 nEnd = nStart + ::std::min( nLength, rString.getLength() - nStart ); + sal_Int32 nColonPos = rString.indexOf( ':', nStart ); + if( (nStart < nColonPos) && (nColonPos + 1 < nEnd) ) + { + return + parseOoxAddress2d( ornStartColumn, ornStartRow, rString, nStart, nColonPos - nStart ) && + parseOoxAddress2d( ornEndColumn, ornEndRow, rString, nColonPos + 1, nLength - nColonPos - 1 ); + } + + if( parseOoxAddress2d( ornStartColumn, ornStartRow, rString, nStart, nLength ) ) + { + ornEndColumn = ornStartColumn; + ornEndRow = ornStartRow; + return true; + } + + return false; +} + +namespace { + +bool lclAppendUrlChar( OUStringBuffer& orUrl, sal_Unicode cChar, bool bEncodeSpecial ) +{ + // #126855# encode special characters + if( bEncodeSpecial ) switch( cChar ) + { + case '#': orUrl.appendAscii( "%23" ); return true; + case '%': orUrl.appendAscii( "%25" ); return true; + } + orUrl.append( cChar ); + return cChar >= ' '; +} + +} // namespace + +bool AddressConverter::parseBiffTargetUrl( + OUString& orClassName, OUString& orTargetUrl, OUString& orSheetName, + const OUString& rBiffTargetUrl ) +{ + OUStringBuffer aTargetUrl; + OUStringBuffer aSheetName; + + enum + { + STATE_START, + STATE_ENCODED_PATH_START, /// Start of encoded file path. + STATE_ENCODED_PATH, /// Inside encoded file path. + STATE_ENCODED_DRIVE, /// DOS drive letter or start of UNC path. + STATE_ENCODED_URL, /// Encoded URL, e.g. http links. + STATE_UNENCODED, /// Unencoded URL, could be DDE or OLE. + STATE_DDE_OLE, /// Second part of DDE or OLE link. + STATE_FILENAME, /// File name enclosed in brackets. + STATE_SHEETNAME, /// Sheet name following enclosed file name. + STATE_UNSUPPORTED, /// Unsupported special paths. + STATE_ERROR + } + eState = STATE_START; + + const sal_Unicode* pcChar = rBiffTargetUrl.getStr(); + const sal_Unicode* pcEnd = pcChar + rBiffTargetUrl.getLength(); + for( ; (eState != STATE_ERROR) && (pcChar < pcEnd) && (*pcChar != 0); ++pcChar ) + { + sal_Unicode cChar = *pcChar; + switch( eState ) + { + case STATE_START: + if( (cChar == mcUrlThisWorkbook) || (cChar == mcUrlThisSheet) ) + { + if( pcChar + 1 < pcEnd ) eState = STATE_ERROR; + } + else if( cChar == mcUrlExternal ) + eState = (pcChar + 1 < pcEnd) ? STATE_ENCODED_PATH_START : STATE_ERROR; + else if( cChar == mcUrlInternal ) + eState = (pcChar + 1 < pcEnd) ? STATE_SHEETNAME : STATE_ERROR; + else + eState = lclAppendUrlChar( aTargetUrl, cChar, true ) ? STATE_UNENCODED : STATE_ERROR; + break; + + case STATE_ENCODED_PATH_START: + if( cChar == BIFF_URL_DRIVE ) + eState = STATE_ENCODED_DRIVE; + else if( cChar == BIFF_URL_ROOT ) + { + aTargetUrl.append( sal_Unicode( '/' ) ); + eState = STATE_ENCODED_PATH; + } + else if( cChar == BIFF_URL_PARENT ) + aTargetUrl.appendAscii( "../" ); + else if( cChar == BIFF_URL_RAW ) + eState = STATE_ENCODED_URL; + else if( cChar == BIFF_URL_INSTALL ) + eState = STATE_UNSUPPORTED; + else if( cChar == BIFF_URL_INSTALL2 ) + eState = STATE_UNSUPPORTED; + else if( cChar == BIFF_URL_ADDIN ) + eState = STATE_UNSUPPORTED; + else if( (getBiff() == BIFF4) && (cChar == BIFF4_URL_SHEET) ) + eState = STATE_SHEETNAME; + else if( cChar == '[' ) + eState = STATE_FILENAME; + else if( lclAppendUrlChar( aTargetUrl, cChar, true ) ) + eState = STATE_ENCODED_PATH; + else + eState = STATE_ERROR; + break; + + case STATE_ENCODED_PATH: + if( cChar == BIFF_URL_SUBDIR ) + aTargetUrl.append( sal_Unicode( '/' ) ); + else if( cChar == '[' ) + eState = STATE_FILENAME; + else if( !lclAppendUrlChar( aTargetUrl, cChar, true ) ) + eState = STATE_ERROR; + break; + + case STATE_ENCODED_DRIVE: + if( cChar == BIFF_URL_UNC ) + { + aTargetUrl.appendAscii( "file://" ); + eState = STATE_ENCODED_PATH; + } + else + { + aTargetUrl.appendAscii( "file:///" ); + eState = lclAppendUrlChar( aTargetUrl, cChar, false ) ? STATE_ENCODED_PATH : STATE_ERROR; + aTargetUrl.appendAscii( ":/" ); + } + break; + + case STATE_ENCODED_URL: + { + sal_Int32 nLength = cChar; + if( nLength + 1 == pcEnd - pcChar ) + aTargetUrl.append( pcChar + 1, nLength ); + else + eState = STATE_ERROR; + } + break; + + case STATE_UNENCODED: + if( cChar == BIFF_URL_SUBDIR ) + { + orClassName = aTargetUrl.makeStringAndClear(); + eState = STATE_DDE_OLE; + } + else if( cChar == '[' ) + eState = STATE_FILENAME; + else if( !lclAppendUrlChar( aTargetUrl, cChar, true ) ) + eState = STATE_ERROR; + break; + + case STATE_DDE_OLE: + if( !lclAppendUrlChar( aTargetUrl, cChar, true ) ) + eState = STATE_ERROR; + break; + + case STATE_FILENAME: + if( cChar == ']' ) + eState = STATE_SHEETNAME; + else if( !lclAppendUrlChar( aTargetUrl, cChar, true ) ) + eState = STATE_ERROR; + break; + + case STATE_SHEETNAME: + if( !lclAppendUrlChar( aSheetName, cChar, false ) ) + eState = STATE_ERROR; + break; + + case STATE_UNSUPPORTED: + pcChar = pcEnd - 1; + break; + + case STATE_ERROR: + break; + } + } + + orTargetUrl = aTargetUrl.makeStringAndClear(); + orSheetName = aSheetName.makeStringAndClear(); + + OSL_ENSURE( (eState != STATE_ERROR) && (pcChar == pcEnd), + OStringBuffer( "AddressConverter::parseBiffTargetUrl - parser error in target \"" ). + append( OUStringToOString( rBiffTargetUrl, RTL_TEXTENCODING_UTF8 ) ).append( '"' ).getStr() ); + return (eState != STATE_ERROR) && (eState != STATE_UNSUPPORTED) && (pcChar == pcEnd); +} + +// ---------------------------------------------------------------------------- + +bool AddressConverter::checkCol( sal_Int32 nCol, bool bTrackOverflow ) +{ + bool bValid = (0 <= nCol) && (nCol <= maMaxPos.Column); + if( !bValid && bTrackOverflow ) + mbColOverflow = true; + return bValid; +} + +bool AddressConverter::checkRow( sal_Int32 nRow, bool bTrackOverflow ) +{ + bool bValid = (0 <= nRow) && (nRow <= maMaxPos.Row); + if( !bValid && bTrackOverflow ) + mbRowOverflow = true; + return bValid; +} + +bool AddressConverter::checkTab( sal_Int16 nSheet, bool bTrackOverflow ) +{ + bool bValid = (0 <= nSheet) && (nSheet <= maMaxPos.Sheet); + if( !bValid && bTrackOverflow ) + mbTabOverflow |= (nSheet > maMaxPos.Sheet); // do not warn for deleted refs (-1) + return bValid; +} + +// ---------------------------------------------------------------------------- + +bool AddressConverter::checkCellAddress( const CellAddress& rAddress, bool bTrackOverflow ) +{ + return + checkTab( rAddress.Sheet, bTrackOverflow ) && + checkCol( rAddress.Column, bTrackOverflow ) && + checkRow( rAddress.Row, bTrackOverflow ); +} + +bool AddressConverter::convertToCellAddressUnckecked( CellAddress& orAddress, + const OUString& rString, sal_Int16 nSheet ) +{ + orAddress.Sheet = nSheet; + return parseOoxAddress2d( orAddress.Column, orAddress.Row, rString ); +} + +bool AddressConverter::convertToCellAddress( CellAddress& orAddress, + const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow ) +{ + return + convertToCellAddressUnckecked( orAddress, rString, nSheet ) && + checkCellAddress( orAddress, bTrackOverflow ); +} + +CellAddress AddressConverter::createValidCellAddress( + const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow ) +{ + CellAddress aAddress; + if( !convertToCellAddress( aAddress, rString, nSheet, bTrackOverflow ) ) + { + aAddress.Sheet = getLimitedValue< sal_Int16, sal_Int16 >( nSheet, 0, maMaxPos.Sheet ); + aAddress.Column = ::std::min( aAddress.Column, maMaxPos.Column ); + aAddress.Row = ::std::min( aAddress.Row, maMaxPos.Row ); + } + return aAddress; +} + +void AddressConverter::convertToCellAddressUnckecked( CellAddress& orAddress, + const BinAddress& rBinAddress, sal_Int16 nSheet ) +{ + orAddress.Sheet = nSheet; + orAddress.Column = rBinAddress.mnCol; + orAddress.Row = rBinAddress.mnRow; +} + +bool AddressConverter::convertToCellAddress( CellAddress& orAddress, + const BinAddress& rBinAddress, sal_Int16 nSheet, bool bTrackOverflow ) +{ + convertToCellAddressUnckecked( orAddress, rBinAddress, nSheet ); + return checkCellAddress( orAddress, bTrackOverflow ); +} + +CellAddress AddressConverter::createValidCellAddress( + const BinAddress& rBinAddress, sal_Int16 nSheet, bool bTrackOverflow ) +{ + CellAddress aAddress; + if( !convertToCellAddress( aAddress, rBinAddress, nSheet, bTrackOverflow ) ) + { + aAddress.Sheet = getLimitedValue< sal_Int16, sal_Int16 >( nSheet, 0, maMaxPos.Sheet ); + aAddress.Column = getLimitedValue< sal_Int32, sal_Int32 >( rBinAddress.mnCol, 0, maMaxPos.Column ); + aAddress.Row = getLimitedValue< sal_Int32, sal_Int32 >( rBinAddress.mnRow, 0, maMaxPos.Row ); + } + return aAddress; +} + +// ---------------------------------------------------------------------------- + +bool AddressConverter::checkCellRange( const CellRangeAddress& rRange, bool bTrackOverflow ) +{ + checkCol( rRange.EndColumn, bTrackOverflow ); + checkRow( rRange.EndRow, bTrackOverflow ); + return + checkTab( rRange.Sheet, bTrackOverflow ) && + checkCol( rRange.StartColumn, bTrackOverflow ) && + checkRow( rRange.StartRow, bTrackOverflow ); +} + +bool AddressConverter::validateCellRange( CellRangeAddress& orRange, bool bTrackOverflow ) +{ + if( orRange.StartColumn > orRange.EndColumn ) + ::std::swap( orRange.StartColumn, orRange.EndColumn ); + if( orRange.StartRow > orRange.EndRow ) + ::std::swap( orRange.StartRow, orRange.EndRow ); + if( !checkCellRange( orRange, bTrackOverflow ) ) + return false; + if( orRange.EndColumn > maMaxPos.Column ) + orRange.EndColumn = maMaxPos.Column; + if( orRange.EndRow > maMaxPos.Row ) + orRange.EndRow = maMaxPos.Row; + return true; +} + +bool AddressConverter::convertToCellRangeUnchecked( CellRangeAddress& orRange, + const OUString& rString, sal_Int16 nSheet ) +{ + orRange.Sheet = nSheet; + return parseOoxRange2d( orRange.StartColumn, orRange.StartRow, orRange.EndColumn, orRange.EndRow, rString ); +} + +bool AddressConverter::convertToCellRange( CellRangeAddress& orRange, + const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow ) +{ + return + convertToCellRangeUnchecked( orRange, rString, nSheet ) && + validateCellRange( orRange, bTrackOverflow ); +} + +void AddressConverter::convertToCellRangeUnchecked( CellRangeAddress& orRange, + const BinRange& rBinRange, sal_Int16 nSheet ) +{ + orRange.Sheet = nSheet; + orRange.StartColumn = rBinRange.maFirst.mnCol; + orRange.StartRow = rBinRange.maFirst.mnRow; + orRange.EndColumn = rBinRange.maLast.mnCol; + orRange.EndRow = rBinRange.maLast.mnRow; +} + +bool AddressConverter::convertToCellRange( CellRangeAddress& orRange, + const BinRange& rBinRange, sal_Int16 nSheet, bool bTrackOverflow ) +{ + convertToCellRangeUnchecked( orRange, rBinRange, nSheet ); + return validateCellRange( orRange, bTrackOverflow ); +} + +// ---------------------------------------------------------------------------- + +bool AddressConverter::checkCellRangeList( const ApiCellRangeList& rRanges, bool bTrackOverflow ) +{ + for( ApiCellRangeList::const_iterator aIt = rRanges.begin(), aEnd = rRanges.end(); aIt != aEnd; ++aIt ) + if( !checkCellRange( *aIt, bTrackOverflow ) ) + return false; + return true; +} + +void AddressConverter::validateCellRangeList( ApiCellRangeList& orRanges, bool bTrackOverflow ) +{ + for( size_t nIndex = orRanges.size(); nIndex > 0; --nIndex ) + if( !validateCellRange( orRanges[ nIndex - 1 ], bTrackOverflow ) ) + orRanges.erase( orRanges.begin() + nIndex - 1 ); +} + +void AddressConverter::convertToCellRangeList( ApiCellRangeList& orRanges, + const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow ) +{ + sal_Int32 nPos = 0; + sal_Int32 nLen = rString.getLength(); + CellRangeAddress aRange; + while( (0 <= nPos) && (nPos < nLen) ) + { + OUString aToken = rString.getToken( 0, ' ', nPos ); + if( (aToken.getLength() > 0) && convertToCellRange( aRange, aToken, nSheet, bTrackOverflow ) ) + orRanges.push_back( aRange ); + } +} + +void AddressConverter::convertToCellRangeList( ApiCellRangeList& orRanges, + const BinRangeList& rBinRanges, sal_Int16 nSheet, bool bTrackOverflow ) +{ + CellRangeAddress aRange; + for( BinRangeList::const_iterator aIt = rBinRanges.begin(), aEnd = rBinRanges.end(); aIt != aEnd; ++aIt ) + if( convertToCellRange( aRange, *aIt, nSheet, bTrackOverflow ) ) + orRanges.push_back( aRange ); +} + +// private -------------------------------------------------------------------- + +void AddressConverter::initializeMaxPos( + sal_Int16 nMaxXlsTab, sal_Int32 nMaxXlsCol, sal_Int32 nMaxXlsRow ) +{ + maMaxXlsPos.Sheet = nMaxXlsTab; + maMaxXlsPos.Column = nMaxXlsCol; + maMaxXlsPos.Row = nMaxXlsRow; + + // maximum cell position in Calc + try + { + Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW ); + Reference< XCellRangeAddressable > xAddressable( xSheetsIA->getByIndex( 0 ), UNO_QUERY_THROW ); + CellRangeAddress aRange = xAddressable->getRangeAddress(); + maMaxApiPos = CellAddress( API_MAXTAB, aRange.EndColumn, aRange.EndRow ); + maMaxPos = getBaseFilter().isImportFilter() ? maMaxApiPos : maMaxXlsPos; + } + catch( Exception& ) + { + OSL_ENSURE( false, "AddressConverter::AddressConverter - cannot get sheet limits" ); + } +} + +void AddressConverter::initializeEncodedUrl( + sal_Unicode cUrlThisWorkbook, sal_Unicode cUrlExternal, + sal_Unicode cUrlThisSheet, sal_Unicode cUrlInternal ) +{ + mcUrlThisWorkbook = cUrlThisWorkbook; + mcUrlExternal = cUrlExternal; + mcUrlThisSheet = cUrlThisSheet; + mcUrlInternal = cUrlInternal; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/autofiltercontext.cxx b/oox/source/xls/autofiltercontext.cxx new file mode 100644 index 000000000000..99f56575dc01 --- /dev/null +++ b/oox/source/xls/autofiltercontext.cxx @@ -0,0 +1,770 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: autofiltercontext.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:07 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/autofiltercontext.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/xls/addressconverter.hxx" +#include <rtl/ustrbuf.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include <com/sun/star/sheet/XDatabaseRanges.hpp> +#include <com/sun/star/sheet/XSheetFilterDescriptor.hpp> +#include <com/sun/star/sheet/FilterOperator.hpp> +#include <com/sun/star/sheet/FilterConnection.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/i18n/XLocaleData.hpp> + +#define DEBUG_OOX_AUTOFILTER 0 + +#if USE_SC_MULTI_STRING_FILTER_PATCH +#include <com/sun/star/sheet/XExtendedSheetFilterDescriptor.hpp> +#include <com/sun/star/sheet/TableFilterFieldNormal.hpp> +#include <com/sun/star/sheet/TableFilterFieldMultiString.hpp> +using ::com::sun::star::sheet::TableFilterFieldNormal; +using ::com::sun::star::sheet::TableFilterFieldMultiString; +using ::com::sun::star::sheet::XExtendedSheetFilterDescriptor; +#else +#include <com/sun/star/sheet/TableFilterField.hpp> +using ::com::sun::star::sheet::TableFilterField; +#endif + +#if DEBUG_OOX_AUTOFILTER +#include <stdio.h> +#endif + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::std::list; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNamed; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::table::XCellRange; +using ::com::sun::star::sheet::XDatabaseRange; +using ::com::sun::star::sheet::XDatabaseRanges; +using ::com::sun::star::sheet::XSheetFilterDescriptor; +using ::com::sun::star::i18n::LocaleDataItem; +using ::com::sun::star::i18n::XLocaleData; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::lang::Locale; + +namespace oox { +namespace xls { + +// ============================================================================ + +FilterFieldItem::FilterFieldItem() : +#if USE_SC_MULTI_STRING_FILTER_PATCH + mpField(new TableFilterFieldNormal), +#else + mpField(new TableFilterField), +#endif + meType(NORMAL) +{ +} + +FilterFieldItem::FilterFieldItem(Type eType) : + meType(eType) +{ +#if USE_SC_MULTI_STRING_FILTER_PATCH + switch ( eType ) + { + case MULTI_STRING: + mpField.reset(new TableFilterFieldMultiString); + break; + case NORMAL: + mpField.reset(new TableFilterFieldNormal); + break; + default: + mpField.reset(new TableFilterFieldNormal); + } +#else + mpField.reset(new TableFilterField); + meType = NORMAL; +#endif +} + +// ============================================================================ + +OoxAutoFilterContext::OoxAutoFilterContext( const OoxWorksheetFragmentBase& rFragment ) : + OoxWorksheetContextBase( rFragment ), + mbValidAddress( false ), + mbUseRegex( false ), + mbShowBlank( false ), + mbConnectionAnd( false ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxAutoFilterContext::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( autoFilter ): + return (nElement == XLS_TOKEN( filterColumn )); + case XLS_TOKEN( filterColumn ): + return (nElement == XLS_TOKEN( filters )) || + (nElement == XLS_TOKEN( customFilters )) || + (nElement == XLS_TOKEN( top10 )) || + (nElement == XLS_TOKEN( dynamicFilter )); + case XLS_TOKEN( filters ): + return (nElement == XLS_TOKEN( filter )); + case XLS_TOKEN( customFilters ): + return (nElement == XLS_TOKEN( customFilter )); + } + return false; +} + +void OoxAutoFilterContext::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( autoFilter ): + importAutoFilter( rAttribs ); + break; + case XLS_TOKEN( filterColumn ): + if ( mbValidAddress ) + importFilterColumn( rAttribs ); + break; + case XLS_TOKEN( filters ): + if ( mbValidAddress ) + importFilters( rAttribs ); + break; + case XLS_TOKEN( filter ): + if ( mbValidAddress ) + importFilter( rAttribs ); + break; + case XLS_TOKEN( customFilters ): + if ( mbValidAddress ) + importCustomFilters( rAttribs ); + break; + case XLS_TOKEN( customFilter ): + if ( mbValidAddress ) + importCustomFilter( rAttribs ); + break; + case XLS_TOKEN( top10 ): + if ( mbValidAddress ) + importTop10( rAttribs ); + break; + case XLS_TOKEN( dynamicFilter ): + if ( mbValidAddress ) + importDynamicFilter( rAttribs ); + break; + } +} + +void OoxAutoFilterContext::onEndElement( const OUString& /*rChars*/ ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( autoFilter ): + maybeShowBlank(); + setAutoFilter(); + break; + case XLS_TOKEN( filters ): + setFilterNames(); + break; + } +} + +#if DEBUG_OOX_AUTOFILTER +static void lclPrintNormalField( +#if USE_SC_MULTI_STRING_FILTER_PATCH + TableFilterFieldNormal* pField +#else + TableFilterField* pField +#endif +) +{ + using namespace ::com::sun::star::sheet; + + printf(" Operator: "); + switch ( pField->Operator ) + { + case FilterOperator_EQUAL: + printf("EQUAL"); + break; + case FilterOperator_NOT_EQUAL: + printf("NOT_EQUAL"); + break; + case com::sun::star::sheet::FilterOperator_GREATER: + printf("GREATER"); + break; + case com::sun::star::sheet::FilterOperator_GREATER_EQUAL: + printf("GREATER_EQUAL"); + break; + case FilterOperator_LESS: + printf("LESS"); + break; + case FilterOperator_LESS_EQUAL: + printf("LESS_EQUAL"); + break; + case FilterOperator_NOT_EMPTY: + printf("NOT_EMPTY"); + break; + case FilterOperator_EMPTY: + printf("EMPTY"); + break; + case FilterOperator_BOTTOM_PERCENT: + printf("BOTTOM_PERCENT"); + break; + case FilterOperator_BOTTOM_VALUES: + printf("BOTTOM_VALUES"); + break; + case FilterOperator_TOP_PERCENT: + printf("TOP_PERCENT"); + break; + case FilterOperator_TOP_VALUES: + printf("TOP_VALUES"); + break; + default: + printf("other"); + } + printf("\n"); + + printf(" StringValue: %s\n", + OUStringToOString(pField->StringValue, RTL_TEXTENCODING_UTF8).getStr()); + + printf(" NumericValue: %g\n", pField->NumericValue); + + printf(" IsNumeric: "); + if (pField->IsNumeric) + printf("yes\n"); + else + printf("no\n"); +} + +static void lclPrintFieldConnection( ::com::sun::star::sheet::FilterConnection eConn ) +{ + using namespace ::com::sun::star::sheet; + + printf(" Connection: "); + switch ( eConn ) + { + case FilterConnection_AND: + printf("AND"); + break; + case FilterConnection_OR: + printf("OR"); + break; + case FilterConnection_MAKE_FIXED_SIZE: + printf("MAKE_FIXED_SIZE"); + break; + default: + printf("other"); + } + printf("\n"); +} + +static void lclPrintFilterField( const FilterFieldItem& aItem ) +{ + using namespace ::com::sun::star::sheet; + + printf("----------------------------------------\n"); +#if USE_SC_MULTI_STRING_FILTER_PATCH + { + // Print common fields first. + + TableFilterFieldBase* pField = aItem.mpField.get(); + printf(" Field: %ld\n", pField->Field); + lclPrintFieldConnection(pField->Connection); + } + switch ( aItem.meType ) + { + case FilterFieldItem::NORMAL: + { + TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); + lclPrintNormalField(pField); + } + break; + case FilterFieldItem::MULTI_STRING: + { + TableFilterFieldMultiString* pMultiStrField = static_cast<TableFilterFieldMultiString*>(aItem.mpField.get()); + sal_Int32 nSize = pMultiStrField->StringSet.getLength(); + printf(" StringSet:\n"); + for ( sal_Int32 i = 0; i < nSize; ++i ) + { + printf(" * %s\n", + OUStringToOString(pMultiStrField->StringSet[i], RTL_TEXTENCODING_UTF8).getStr()); + } + } + break; + } +#else + TableFilterField* pField = aItem.mpField.get(); + printf(" Field: %ld\n", pField->Field); + lclPrintFieldConnection(pField->Connection); + lclPrintNormalField(pField); + +#endif + fflush(stdout); +} +#endif + +void OoxAutoFilterContext::initialize() +{ + maFields.clear(); + maFilterNames.clear(); + mbValidAddress = mbShowBlank = mbUseRegex = mbConnectionAnd = false; +} + +void OoxAutoFilterContext::setAutoFilter() +{ + using namespace ::com::sun::star::sheet; + + // Name this built-in database. + OUStringBuffer sDataAreaNameBuf( CREATE_OUSTRING("Excel_BuiltIn__FilterDatabase_ ") ); + sDataAreaNameBuf.append( static_cast<sal_Int32>(getSheetIndex()+1) ); + + OUString sDataAreaName = sDataAreaNameBuf.makeStringAndClear(); + Reference< XCellRange > xCellRange = getCellRange( maAutoFilterRange ); + + // Create a new database range, add filters to it and refresh the database + // for that to take effect. + + Reference< XDatabaseRanges > xDBRanges; + { + PropertySet aDocProp( getDocument() ); + aDocProp.getProperty( xDBRanges, CREATE_OUSTRING("DatabaseRanges") ); + if ( !xDBRanges.is() ) + { + OSL_ENSURE(false, "OoxAutoFilterContext::setAutoFilter: DBRange empty"); + return; + } + } + + Reference< XNameAccess > xNA( xDBRanges, UNO_QUERY_THROW ); + if ( !xNA->hasByName( sDataAreaName ) ) + xDBRanges->addNewByName( sDataAreaName, maAutoFilterRange ); + + Reference< XDatabaseRange > xDB( xNA->getByName( sDataAreaName ), UNO_QUERY ); + if ( xDB.is() ) + { + PropertySet aProp( xDB ); + aProp.setProperty( CREATE_OUSTRING("AutoFilter"), true ); + } + + sal_Int32 nSize = maFields.size(); + sal_Int32 nMaxFieldCount = nSize; + Reference< XSheetFilterDescriptor > xDescriptor = xDB->getFilterDescriptor(); + if ( xDescriptor.is() ) + { + PropertySet aProp( xDescriptor ); + aProp.setProperty( CREATE_OUSTRING("ContainsHeader"), true ); + aProp.setProperty( CREATE_OUSTRING("UseRegularExpressions"), mbUseRegex ); + aProp.getProperty( nMaxFieldCount, CREATE_OUSTRING("MaxFieldCount") ); + } + else + { + OSL_ENSURE(false, "OoxAutoFilterContext::setAutoFilter: descriptor is empty"); + return; + } + + // Unpack all column field items into a sequence. +#if USE_SC_MULTI_STRING_FILTER_PATCH + Reference< XExtendedSheetFilterDescriptor > xExtDescriptor( xDescriptor, UNO_QUERY ); + if ( !xExtDescriptor.is() ) + { + OSL_ENSURE(false, "OoxAutoFilterContext::setAutoFilter: extended descriptor is empty"); + return; + } + + xExtDescriptor->begin(); + + list< FilterFieldItem >::const_iterator itr = maFields.begin(), itrEnd = maFields.end(); + for (sal_Int32 i = 0; itr != itrEnd && i < nMaxFieldCount; ++itr, ++i) + { +#if DEBUG_OOX_AUTOFILTER + lclPrintFilterField(*itr); +#endif + switch ( itr->meType ) + { + case oox::xls::FilterFieldItem::MULTI_STRING: + { + // multi-string filter type + TableFilterFieldMultiString* pField = static_cast<TableFilterFieldMultiString*>( itr->mpField.get() ); + xExtDescriptor->addFilterFieldMultiString( *pField ); + } + break; + case oox::xls::FilterFieldItem::NORMAL: + default: + // normal filter type + TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>( itr->mpField.get() ); + xExtDescriptor->addFilterFieldNormal( *pField ); + } + } + xExtDescriptor->commit(); + +#else + Sequence< TableFilterField > aFields(nSize); + list< FilterFieldItem >::const_iterator itr = maFields.begin(), itrEnd = maFields.end(); + for (sal_Int32 i = 0; itr != itrEnd && i < nMaxFieldCount; ++itr, ++i) + { +#if DEBUG_OOX_AUTOFILTER + lclPrintFilterField( *itr ); +#endif + aFields[i] = *itr->mpField; + } + xDescriptor->setFilterFields( aFields ); +#endif + xDB->refresh(); +} + +void OoxAutoFilterContext::maybeShowBlank() +{ + using namespace ::com::sun::star::sheet; + + if ( !mbShowBlank ) + return; + +#if USE_SC_MULTI_STRING_FILTER_PATCH + FilterFieldItem aItem(FilterFieldItem::NORMAL); + TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); + pField->Field = mnCurColID; + pField->Operator = FilterOperator_EMPTY; + pField->Connection = FilterConnection_AND; + pField->IsNumeric = false; +#else + FilterFieldItem aItem; + aItem.mpField->Field = mnCurColID; + aItem.mpField->Operator = FilterOperator_EMPTY; + aItem.mpField->Connection = FilterConnection_AND; + aItem.mpField->IsNumeric = false; +#endif + maFields.push_back(aItem); +} + +void OoxAutoFilterContext::setFilterNames() +{ + using namespace ::com::sun::star::sheet; + + + sal_Int32 size = maFilterNames.size(); + if ( !size ) + return; + +#if USE_SC_MULTI_STRING_FILTER_PATCH + Sequence< OUString > aStrList(size); + list< OUString >::const_iterator itr = maFilterNames.begin(), itrEnd = maFilterNames.end(); + for (sal_Int32 i = 0; itr != itrEnd; ++itr, ++i) + aStrList[i] = *itr; + + FilterFieldItem aItem(FilterFieldItem::MULTI_STRING); + TableFilterFieldMultiString* pField = static_cast<TableFilterFieldMultiString*>( aItem.mpField.get() ); + pField->Field = mnCurColID; + pField->Connection = FilterConnection_AND; + pField->StringSet = aStrList; + + maFields.push_back(aItem); +#else + static const OUString sSep = CREATE_OUSTRING("|"); + + OUStringBuffer buf; + if ( size > 1 ) + { + buf.append( CREATE_OUSTRING("^(") ); + mbUseRegex = true; + } + + list< OUString >::const_iterator itr = maFilterNames.begin(), itrEnd = maFilterNames.end(); + bool bFirst = true; + for (; itr != itrEnd; ++itr) + { + if (bFirst) + bFirst = false; + else + buf.append( sSep ); + buf.append( *itr ); + } + if ( size > 1 ) + buf.append( CREATE_OUSTRING(")$") ); + + FilterFieldItem aItem; + aItem.mpField->Field = mnCurColID; + aItem.mpField->StringValue = buf.makeStringAndClear(); + aItem.mpField->Operator = FilterOperator_EQUAL; + aItem.mpField->Connection = FilterConnection_AND; + aItem.mpField->IsNumeric = false; + maFields.push_back(aItem); +#endif +} + +void OoxAutoFilterContext::importAutoFilter( const AttributeList& rAttribs ) +{ + initialize(); + + mbValidAddress = getAddressConverter().convertToCellRange( + maAutoFilterRange, rAttribs.getString( XML_ref ), getSheetIndex(), true ); +} + +void OoxAutoFilterContext::importFilterColumn( const AttributeList& rAttribs ) +{ + // hiddenButton and showButton attributes are not used for now. + mnCurColID = rAttribs.getInteger( XML_colId, -1 ); +} + +void OoxAutoFilterContext::importTop10( const AttributeList& rAttribs ) +{ + using namespace ::com::sun::star::sheet; + + // filterVal attribute is not necessarily, since Calc also supports top 10 + // and top 10% filter type. + FilterFieldItem aItem; +#if USE_SC_MULTI_STRING_FILTER_PATCH + TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); +#else + TableFilterField* pField = aItem.mpField.get(); +#endif + pField->Field = mnCurColID; + + bool bPercent = rAttribs.getBool( XML_percent, false ); + bool bTop = rAttribs.getBool( XML_top, true ); + pField->NumericValue = rAttribs.getDouble( XML_val, 0.0 ); + pField->IsNumeric = true; + + // When top10 filter item is present, that's the only filter item for that column. + if ( bTop ) + if ( bPercent ) + pField->Operator = FilterOperator_TOP_PERCENT; + else + pField->Operator = FilterOperator_TOP_VALUES; + else + if ( bPercent ) + pField->Operator = FilterOperator_BOTTOM_PERCENT; + else + pField->Operator = FilterOperator_BOTTOM_VALUES; + + maFields.push_back(aItem); +} + +void OoxAutoFilterContext::importCustomFilters( const AttributeList& rAttribs ) +{ + // OR is default when the 'and' attribute is absent. + mbConnectionAnd = rAttribs.getBool( XML_and, false ); +} + +/** Do a best-effort guess of whether or not the given string is numerical. */ +static bool lclIsNumeric( const OUString& _str, const LocaleDataItem& aLocaleItem ) +{ + OUString str = _str.trim(); + sal_Int32 size = str.getLength(); + + if ( !size ) + // Empty string. This can't be a number. + return false; + + // Get the decimal separator for the current locale. + const OUString& sep = aLocaleItem.decimalSeparator; + + bool bDecimalSep = false; + for (sal_Int32 i = 0; i < size; ++i) + { + OUString c = str.copy(i, 1); + if ( !c.compareTo(sep) ) + { + if ( bDecimalSep ) + return false; + else + { + bDecimalSep = true; + continue; + } + } + if ( (0 > c.compareToAscii("0") || 0 < c.compareToAscii("9")) ) + return false; + } + + return true; +} + +/** Convert wildcard characters to regex equivalent. Returns true if any + wildcard character is found. */ +static bool lclWildcard2Regex( OUString& str ) +{ + bool bWCFound = false; + OUStringBuffer buf; + sal_Int32 size = str.getLength(); + buf.ensureCapacity(size + 6); // pure heuristics. + + sal_Unicode dot = '.', star = '*', hat = '^', dollar = '$'; + buf.append(hat); + for (sal_Int32 i = 0; i < size; ++i) + { + OUString c = str.copy(i, 1); + if ( !c.compareToAscii("?") ) + { + buf.append(dot); + bWCFound = true; + } + else if ( !c.compareToAscii("*") ) + { + buf.append(dot); + buf.append(star); + bWCFound = true; + } + else + buf.append(c); + } + buf.append(dollar); + + if (bWCFound) + str = buf.makeStringAndClear(); + + return bWCFound; +} + +/** Translate Excel's filter operator to Calc's. */ +static ::com::sun::star::sheet::FilterOperator lclTranslateFilterOp( sal_Int32 nToken ) +{ + using namespace ::com::sun::star::sheet; + + switch ( nToken ) + { + case XML_equal: + return FilterOperator_EQUAL; + case XML_notEqual: + return FilterOperator_NOT_EQUAL; + case XML_greaterThan: + return FilterOperator_GREATER; + case XML_greaterThanOrEqual: + return FilterOperator_GREATER_EQUAL; + case XML_lessThan: + return FilterOperator_LESS; + case XML_lessThanOrEqual: + return FilterOperator_LESS_EQUAL; + } + return FilterOperator_EQUAL; +} + +void OoxAutoFilterContext::importCustomFilter( const AttributeList& rAttribs ) +{ + using namespace ::com::sun::star::sheet; + + sal_Int32 nToken = rAttribs.getToken( XML_operator, XML_equal ); +#if USE_SC_MULTI_STRING_FILTER_PATCH + FilterFieldItem aItem(FilterFieldItem::NORMAL); + TableFilterFieldNormal* pField = static_cast<TableFilterFieldNormal*>(aItem.mpField.get()); +#else + FilterFieldItem aItem; + TableFilterField* pField = aItem.mpField.get(); +#endif + pField->Field = mnCurColID; + pField->StringValue = rAttribs.getString( XML_val ); + pField->NumericValue = pField->StringValue.toDouble(); + pField->Operator = lclTranslateFilterOp( nToken ); + + if ( nToken == XML_notEqual && !pField->StringValue.compareToAscii(" ") ) + { + // Special case for hiding blanks. Excel translates "hide blanks" to + // (filter if notEqual " "). So, we need to translate it back. + pField->Operator = FilterOperator_NOT_EMPTY; + pField->IsNumeric = false; + maFields.push_back(aItem); + return; + } + + switch ( nToken ) + { + case XML_equal: + case XML_notEqual: + { + Reference< XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory(); + Reference< XLocaleData > xLocale( xFactory->createInstance( + CREATE_OUSTRING("com.sun.star.i18n.LocaleData") ), UNO_QUERY ); + + if ( !xLocale.is() ) + return; + + LocaleDataItem aLocaleItem = xLocale->getLocaleItem( ::com::sun::star::lang::Locale() ); + pField->IsNumeric = lclIsNumeric(pField->StringValue, aLocaleItem); + + if ( !pField->IsNumeric && lclWildcard2Regex(pField->StringValue) ) + mbUseRegex = true; + + maFields.push_back(aItem); + } + break; + + case XML_greaterThan: + case XML_greaterThanOrEqual: + case XML_lessThan: + case XML_lessThanOrEqual: + { + pField->IsNumeric = true; + maFields.push_back(aItem); + } + break; + default: + OSL_ENSURE( false, "OoxAutoFilterContext::importCustomFilter: unhandled case" ); + } +} + +void OoxAutoFilterContext::importFilters( const AttributeList& rAttribs ) +{ + // blank (boolean) and calendarType attributes can be present, but not used for now. + + mbShowBlank = rAttribs.getBool( XML_blank, false ); + maFilterNames.clear(); +} + +void OoxAutoFilterContext::importFilter( const AttributeList& rAttribs ) +{ + if (mnCurColID == -1) + return; + + OUString value = rAttribs.getString( XML_val ); + if ( value.getLength() ) + maFilterNames.push_back(value); +} + +void OoxAutoFilterContext::importDynamicFilter( const AttributeList& /*rAttribs*/ ) +{ + // not implemented yet - Calc doesn't support this. +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/biffcodec.cxx b/oox/source/xls/biffcodec.cxx new file mode 100644 index 000000000000..0eb0db29aa91 --- /dev/null +++ b/oox/source/xls/biffcodec.cxx @@ -0,0 +1,211 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: biffcodec.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:07 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/biffcodec.hxx" +#include <osl/thread.h> + +using ::rtl::OString; +using ::rtl::OUString; +using ::rtl::OStringToOUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +const OString& BiffCodecHelper::getBiff5WbProtPassword() +{ + static const OString saPass( "VelvetSweatshop" ); + return saPass; +} + +const OUString& BiffCodecHelper::getBiff8WbProtPassword() +{ + static const OUString saPass = OStringToOUString( getBiff5WbProtPassword(), RTL_TEXTENCODING_ASCII_US ); + return saPass; +} + +// ============================================================================ + +BiffDecoderBase::BiffDecoderBase( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnError( CODEC_ERROR_UNSUPP_CRYPT ) +{ +} + +BiffDecoderBase::~BiffDecoderBase() +{ +} + +void BiffDecoderBase::decode( sal_uInt8* pnDestData, const sal_uInt8* pnSrcData, sal_Int64 nStreamPos, sal_uInt16 nBytes ) +{ + if( pnDestData && pnSrcData && (nBytes > 0) ) + { + if( isValid() ) + implDecode( pnDestData, pnSrcData, nStreamPos, nBytes ); + else + memcpy( pnDestData, pnSrcData, nBytes ); + } +} + +void BiffDecoderBase::setHasValidPassword( bool bValid ) +{ + mnError = bValid ? CODEC_OK : CODEC_ERROR_WRONG_PASS; +} + +// ============================================================================ + +BiffDecoder_XOR::BiffDecoder_XOR( const WorkbookHelper& rHelper, sal_uInt16 nKey, sal_uInt16 nHash ) : + BiffDecoderBase( rHelper ), + maCodec( ::oox::core::BinaryCodec_XOR::CODEC_EXCEL ) +{ + init( BiffCodecHelper::getBiff5WbProtPassword(), nKey, nHash ); + if( !isValid() ) + { + OString aPass = OUStringToOString( queryPassword(), osl_getThreadTextEncoding() ); + init( aPass, nKey, nHash ); + } +} + +void BiffDecoder_XOR::init( const OString& rPass, sal_uInt16 nKey, sal_uInt16 nHash ) +{ + sal_Int32 nLen = rPass.getLength(); + bool bValid = (0 < nLen) && (nLen < 16); + + if( bValid ) + { + // copy byte string to sal_uInt8 array + sal_uInt8 pnPassw[ 16 ]; + memset( pnPassw, 0, sizeof( pnPassw ) ); + memcpy( pnPassw, rPass.getStr(), static_cast< size_t >( nLen ) ); + + // init codec + maCodec.initKey( pnPassw ); + bValid = maCodec.verifyKey( nKey, nHash ); + } + + setHasValidPassword( bValid ); +} + +void BiffDecoder_XOR::implDecode( sal_uInt8* pnDestData, const sal_uInt8* pnSrcData, sal_Int64 nStreamPos, sal_uInt16 nBytes ) +{ + maCodec.startBlock(); + maCodec.skip( static_cast< sal_Int32 >( (nStreamPos + nBytes) & 0x0F ) ); + maCodec.decode( pnDestData, pnSrcData, nBytes ); +} + +// ============================================================================ + +namespace { + +/** Returns the block index of the passed stream position for RCF decryption. */ +sal_Int32 lclGetRcfBlock( sal_Int64 nStreamPos ) +{ + return static_cast< sal_Int32 >( nStreamPos / BIFF_RCF_BLOCKSIZE ); +} + +/** Returns the offset of the passed stream position in a block for RCF decryption. */ +sal_Int32 lclGetRcfOffset( sal_Int64 nStreamPos ) +{ + return static_cast< sal_Int32 >( nStreamPos % BIFF_RCF_BLOCKSIZE ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +BiffDecoder_RCF::BiffDecoder_RCF( const WorkbookHelper& rHelper, + sal_uInt8 pnDocId[ 16 ], sal_uInt8 pnSaltData[ 16 ], sal_uInt8 pnSaltHash[ 16 ] ) : + BiffDecoderBase( rHelper ) +{ + init( BiffCodecHelper::getBiff8WbProtPassword(), pnDocId, pnSaltData, pnSaltHash ); + if( !isValid() ) + init( queryPassword(), pnDocId, pnSaltData, pnSaltHash ); +} + +void BiffDecoder_RCF::init( const OUString& rPass, sal_uInt8 pnDocId[ 16 ], sal_uInt8 pnSaltData[ 16 ], sal_uInt8 pnSaltHash[ 16 ] ) +{ + sal_Int32 nLen = rPass.getLength(); + bool bValid = (0 < nLen) && (nLen < 16); + + if( bValid ) + { + // copy string to sal_uInt16 array + sal_uInt16 pnPassw[ 16 ]; + memset( pnPassw, 0, sizeof( pnPassw ) ); + const sal_Unicode* pcChar = rPass.getStr(); + const sal_Unicode* pcCharEnd = pcChar + nLen; + sal_uInt16* pnCurrPass = pnPassw; + for( ; pcChar < pcCharEnd; ++pcChar, ++pnCurrPass ) + *pnCurrPass = static_cast< sal_uInt16 >( *pcChar ); + + // init codec + maCodec.initKey( pnPassw, pnDocId ); + bValid = maCodec.verifyKey( pnSaltData, pnSaltHash ); + } + + setHasValidPassword( bValid ); +} + +void BiffDecoder_RCF::implDecode( sal_uInt8* pnDestData, const sal_uInt8* pnSrcData, sal_Int64 nStreamPos, sal_uInt16 nBytes ) +{ + sal_uInt8* pnCurrDest = pnDestData; + const sal_uInt8* pnCurrSrc = pnSrcData; + sal_Int64 nCurrPos = nStreamPos; + sal_uInt16 nBytesLeft = nBytes; + while( nBytesLeft > 0 ) + { + // initialize codec for current stream position + maCodec.startBlock( lclGetRcfBlock( nCurrPos ) ); + maCodec.skip( lclGetRcfOffset( nCurrPos ) ); + + // decode the block + sal_uInt16 nBlockLeft = static_cast< sal_uInt16 >( BIFF_RCF_BLOCKSIZE - lclGetRcfOffset( nCurrPos ) ); + sal_uInt16 nDecBytes = ::std::min( nBytesLeft, nBlockLeft ); + maCodec.decode( pnCurrDest, pnCurrSrc, static_cast< sal_Int32 >( nDecBytes ) ); + + // prepare for next block + pnCurrDest += nDecBytes; + pnCurrSrc += nDecBytes; + nCurrPos += nDecBytes; + nBytesLeft = nBytesLeft - nDecBytes; + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/biffdetector.cxx b/oox/source/xls/biffdetector.cxx new file mode 100644 index 000000000000..b30e820fcbf0 --- /dev/null +++ b/oox/source/xls/biffdetector.cxx @@ -0,0 +1,241 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: biffdetector.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/biffdetector.hxx" +#include <algorithm> +#include <rtl/strbuf.hxx> +#include <com/sun/star/io/XInputStream.hpp> +#include <comphelper/mediadescriptor.hxx> +#include "oox/helper/binaryinputstream.hxx" +#include "oox/helper/olestorage.hxx" + +using ::rtl::OUString; +using ::rtl::OStringBuffer; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::beans::PropertyValue; +using ::com::sun::star::io::XInputStream; +using ::comphelper::MediaDescriptor; + +namespace oox { +namespace xls { + +// ============================================================================ + +Sequence< OUString > BiffDetector_getSupportedServiceNames() +{ + Sequence< OUString > aServiceNames( 1 ); + aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.frame.ExtendedTypeDetection" ); + return aServiceNames; +} + +OUString BiffDetector_getImplementationName() +{ + return CREATE_OUSTRING( "com.sun.star.comp.oox.BiffDetector" ); +} + +Reference< XInterface > SAL_CALL BiffDetector_createInstance( const Reference< XMultiServiceFactory >& rxFactory ) throw( Exception ) +{ + return static_cast< ::cppu::OWeakObject* >( new BiffDetector( rxFactory ) ); +} + +// ============================================================================ + +BiffDetector::BiffDetector( const Reference< XMultiServiceFactory >& rxFactory ) : + mxFactory( rxFactory ) +{ +} + +BiffDetector::~BiffDetector() +{ +} + +BiffType BiffDetector::detectStreamBiffVersion( BinaryInputStream& rInStream ) +{ + BiffType eBiff = BIFF_UNKNOWN; + if( rInStream.is() && rInStream.isSeekable() && (rInStream.getLength() > 4) ) + { + sal_Int64 nOldPos = rInStream.tell(); + rInStream.seek( 0 ); + sal_uInt16 nBofId, nBofSize; + rInStream >> nBofId >> nBofSize; + + if( (4 <= nBofSize) && (nBofSize <= 16) && (rInStream.tell() + nBofSize <= rInStream.getLength()) ) + { + switch( nBofId ) + { + case BIFF2_ID_BOF: + eBiff = BIFF2; + break; + case BIFF3_ID_BOF: + eBiff = BIFF3; + break; + case BIFF4_ID_BOF: + eBiff = BIFF4; + break; + case BIFF5_ID_BOF: + { + if( 6 <= nBofSize ) + { + sal_uInt16 nVersion; + rInStream >> nVersion; + // #i23425# #i44031# #i62752# there are some *really* broken documents out there... + switch( nVersion & 0xFF00 ) + { + case 0: eBiff = BIFF5; break; // #i44031# #i62752# + case BIFF_BOF_BIFF2: eBiff = BIFF2; break; + case BIFF_BOF_BIFF3: eBiff = BIFF3; break; + case BIFF_BOF_BIFF4: eBiff = BIFF4; break; + case BIFF_BOF_BIFF5: eBiff = BIFF5; break; + case BIFF_BOF_BIFF8: eBiff = BIFF8; break; + default: OSL_ENSURE( false, + OStringBuffer( "lclDetectStreamBiffVersion - unknown BIFF version: 0x" ). + append( static_cast< sal_Int32 >( nVersion ), 16 ).getStr() ); + } + } + } + break; + // else do nothing, no BIFF stream + } + } + rInStream.seek( nOldPos ); + } + return eBiff; +} + +BiffType BiffDetector::detectStorageBiffVersion( OUString& orWorkbookStreamName, StorageRef xStorage ) +{ + static const OUString saBookName = CREATE_OUSTRING( "Book" ); + static const OUString saWorkbookName = CREATE_OUSTRING( "Workbook" ); + + BiffType eBiff = BIFF_UNKNOWN; + if( xStorage.get() ) + { + if( xStorage->isStorage() ) + { + // try to open the "Book" stream + BinaryInputStream aBookStrm5( xStorage->openInputStream( saBookName ), true ); + BiffType eBookStrm5Biff = detectStreamBiffVersion( aBookStrm5 ); + + // try to open the "Workbook" stream + BinaryInputStream aBookStrm8( xStorage->openInputStream( saWorkbookName ), true ); + BiffType eBookStrm8Biff = detectStreamBiffVersion( aBookStrm8 ); + + // decide which stream to use + if( (eBookStrm8Biff != BIFF_UNKNOWN) && ((eBookStrm5Biff == BIFF_UNKNOWN) || (eBookStrm8Biff > eBookStrm5Biff)) ) + { + /* Only "Workbook" stream exists; or both streams exist, + and "Workbook" has higher BIFF version than "Book" stream. */ + eBiff = eBookStrm8Biff; + orWorkbookStreamName = saWorkbookName; + } + else if( eBookStrm5Biff != BIFF_UNKNOWN ) + { + /* Only "Book" stream exists; or both streams exist, + and "Book" has higher BIFF version than "Workbook" stream. */ + eBiff = eBookStrm5Biff; + orWorkbookStreamName = saBookName; + } + } + else + { + // no storage, try plain input stream from medium (even for BIFF5+) + BinaryInputStream aStrm( xStorage->openInputStream( OUString() ), false ); + eBiff = detectStreamBiffVersion( aStrm ); + orWorkbookStreamName = OUString(); + } + } + + return eBiff; +} + +// com.sun.star.lang.XServiceInfo interface ----------------------------------- + +OUString SAL_CALL BiffDetector::getImplementationName() throw( RuntimeException ) +{ + return BiffDetector_getImplementationName(); +} + +sal_Bool SAL_CALL BiffDetector::supportsService( const OUString& rService ) throw( RuntimeException ) +{ + const Sequence< OUString > aServices( BiffDetector_getSupportedServiceNames() ); + const OUString* pArray = aServices.getConstArray(); + const OUString* pArrayEnd = pArray + aServices.getLength(); + return ::std::find( pArray, pArrayEnd, rService ) != pArrayEnd; +} + +Sequence< OUString > SAL_CALL BiffDetector::getSupportedServiceNames() throw( RuntimeException ) +{ + return BiffDetector_getSupportedServiceNames(); +} + +// com.sun.star.document.XExtendedFilterDetect interface ---------------------- + +OUString SAL_CALL BiffDetector::detect( Sequence< PropertyValue >& rDescriptor ) throw( RuntimeException ) +{ + OUString aTypeName; + + MediaDescriptor aDescriptor( rDescriptor ); + aDescriptor.addInputStream(); + + Reference< XInputStream > xInStrm( aDescriptor[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY ); + if( xInStrm.is() ) + { + OUString aWorkbookName; + StorageRef xStorage( new OleStorage( mxFactory, xInStrm, true ) ); + switch( detectStorageBiffVersion( aWorkbookName, xStorage ) ) + { + case BIFF2: + case BIFF3: + case BIFF4: aTypeName = CREATE_OUSTRING( "calc_MS_Excel_40" ); break; + case BIFF5: aTypeName = CREATE_OUSTRING( "calc_MS_Excel_95" ); break; + case BIFF8: aTypeName = CREATE_OUSTRING( "calc_MS_Excel_97" ); break; + default:; + } + } + + return aTypeName; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/bifffragmenthandler.cxx b/oox/source/xls/bifffragmenthandler.cxx new file mode 100644 index 000000000000..061e213b1092 --- /dev/null +++ b/oox/source/xls/bifffragmenthandler.cxx @@ -0,0 +1,169 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: bifffragmenthandler.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/bifffragmenthandler.hxx" +#include "oox/xls/biffhelper.hxx" +#include "oox/xls/biffinputstream.hxx" + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt16 BIFF_BOF_GLOBALS = 0x0005; /// BIFF5-BIFF8 workbook globals. +const sal_uInt16 BIFF_BOF_MODULE = 0x0006; /// BIFF5-BIFF8 Visual Basic module. +const sal_uInt16 BIFF_BOF_SHEET = 0x0010; /// BIFF2-BIFF8 worksheet/dialog sheet. +const sal_uInt16 BIFF_BOF_CHART = 0x0020; /// BIFF2-BIFF8 chart sheet. +const sal_uInt16 BIFF_BOF_MACRO = 0x0040; /// BIFF4-BIFF8 macro sheet. +const sal_uInt16 BIFF_BOF_WORKSPACE = 0x0100; /// BIFF3-BIFF8 workspace. + +} // namespace + +// ============================================================================ + +BiffFragmentHandler::~BiffFragmentHandler() +{ +} + +bool BiffFragmentHandler::importFragment( BiffInputStream& rStrm ) +{ + // default implementation: skip the entire fragment + return skipFragment( rStrm ); +} + +bool BiffFragmentHandler::isBofRecord( sal_uInt16 nRecId ) +{ + return (nRecId == BIFF2_ID_BOF) || (nRecId == BIFF3_ID_BOF) || (nRecId == BIFF4_ID_BOF) || (nRecId == BIFF5_ID_BOF); +} + +BiffFragmentType BiffFragmentHandler::startFragment( BiffInputStream& rStrm, BiffType eBiff ) +{ + BiffFragmentType eFragment = BIFF_FRAGMENT_UNKNOWN; + if( rStrm.startNextRecord() ) + { + /* #i23425# Don't rely on BOF record ID to read BOF contents, but on + the detected BIFF version. */ + if( isBofRecord( rStrm.getRecId() ) ) + { + // BOF is always written unencrypted + rStrm.enableDecoder( false ); + sal_uInt16 nType = rStrm.skip( 2 ).readuInt16(); + + // decide which fragment types are valid for current BIFF version + switch( eBiff ) + { + case BIFF2: switch( nType ) + { + case BIFF_BOF_CHART: eFragment = BIFF_FRAGMENT_EMPTYSHEET; break; + case BIFF_BOF_MACRO: eFragment = BIFF_FRAGMENT_MACRO; break; + // #i51490# Excel interprets invalid types as worksheet + default: eFragment = BIFF_FRAGMENT_WORKSHEET; + } + break; + + case BIFF3: switch( nType ) + { + case BIFF_BOF_CHART: eFragment = BIFF_FRAGMENT_EMPTYSHEET; break; + case BIFF_BOF_MACRO: eFragment = BIFF_FRAGMENT_MACRO; break; + case BIFF_BOF_WORKSPACE:eFragment = BIFF_FRAGMENT_UNKNOWN; break; + // #i51490# Excel interprets invalid types as worksheet + default: eFragment = BIFF_FRAGMENT_WORKSHEET; + }; + break; + + case BIFF4: switch( nType ) + { + case BIFF_BOF_CHART: eFragment = BIFF_FRAGMENT_EMPTYSHEET; break; + case BIFF_BOF_MACRO: eFragment = BIFF_FRAGMENT_MACRO; break; + case BIFF_BOF_WORKSPACE:eFragment = BIFF_FRAGMENT_WORKSPACE; break; + // #i51490# Excel interprets invalid types as worksheet + default: eFragment = BIFF_FRAGMENT_WORKSHEET; + }; + break; + + case BIFF5: + case BIFF8: switch( nType ) + { + case BIFF_BOF_GLOBALS: eFragment = BIFF_FRAGMENT_GLOBALS; break; + case BIFF_BOF_CHART: eFragment = BIFF_FRAGMENT_CHART; break; + case BIFF_BOF_MACRO: eFragment = BIFF_FRAGMENT_MACRO; break; + case BIFF_BOF_WORKSPACE:eFragment = BIFF_FRAGMENT_UNKNOWN; break; + // #i51490# Excel interprets invalid types as worksheet + default: eFragment = BIFF_FRAGMENT_WORKSHEET; + }; + break; + + case BIFF_UNKNOWN: break; + } + } + } + return eFragment; +} + +bool BiffFragmentHandler::skipFragment( BiffInputStream& rStrm ) +{ + while( rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) ) + if( isBofRecord( rStrm.getRecId() ) ) + skipFragment( rStrm ); + return rStrm.isValid() && (rStrm.getRecId() == BIFF_ID_EOF); +} + +// ============================================================================ + +BiffWorkbookFragmentBase::BiffWorkbookFragmentBase( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +// ============================================================================ + +BiffWorksheetFragmentBase::BiffWorksheetFragmentBase( const WorkbookHelper& rHelper, + ISegmentProgressBarRef xProgressBar, WorksheetType eSheetType, sal_Int32 nSheet ) : + WorksheetHelperRoot( rHelper, xProgressBar, eSheetType, nSheet ) +{ +} + +BiffWorksheetFragmentBase::BiffWorksheetFragmentBase( const WorksheetHelper& rHelper ) : + WorksheetHelperRoot( rHelper ) +{ +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/biffhelper.cxx b/oox/source/xls/biffhelper.cxx new file mode 100644 index 000000000000..fb04a87e2502 --- /dev/null +++ b/oox/source/xls/biffhelper.cxx @@ -0,0 +1,296 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: biffhelper.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/biffhelper.hxx" +#include <algorithm> +#include <rtl/math.hxx> +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/biffoutputstream.hxx" +#include "oox/xls/worksheethelper.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +namespace oox { +namespace xls { + +// GUID ======================================================================= + +BiffGuid::BiffGuid() +{ + ::std::fill_n( mpnData, sizeof( mpnData ), 0 ); +} + +BiffGuid::BiffGuid( + sal_uInt32 nData1, sal_uInt16 nData2, sal_uInt16 nData3, + sal_uInt8 nData41, sal_uInt8 nData42, sal_uInt8 nData43, sal_uInt8 nData44, + sal_uInt8 nData45, sal_uInt8 nData46, sal_uInt8 nData47, sal_uInt8 nData48 ) +{ + // convert to little endian -> makes streaming easy + ByteOrderConverter::writeLittleEndian( mpnData, nData1 ); + ByteOrderConverter::writeLittleEndian( mpnData + 4, nData2 ); + ByteOrderConverter::writeLittleEndian( mpnData + 6, nData3 ); + mpnData[ 8 ] = nData41; + mpnData[ 9 ] = nData42; + mpnData[ 10 ] = nData43; + mpnData[ 11 ] = nData44; + mpnData[ 12 ] = nData45; + mpnData[ 13 ] = nData46; + mpnData[ 14 ] = nData47; + mpnData[ 15 ] = nData48; +} + +bool operator==( const BiffGuid& rGuid1, const BiffGuid& rGuid2 ) +{ + return ::std::equal( rGuid1.mpnData, STATIC_ARRAY_END( rGuid1.mpnData ), rGuid2.mpnData ); +} + +bool operator<( const BiffGuid& rGuid1, const BiffGuid& rGuid2 ) +{ + return ::std::lexicographical_compare( + rGuid1.mpnData, STATIC_ARRAY_END( rGuid1.mpnData ), + rGuid2.mpnData, STATIC_ARRAY_END( rGuid2.mpnData ) ); +} + +BiffInputStream& operator>>( BiffInputStream& rStrm, BiffGuid& rGuid ) +{ + rStrm.read( rGuid.mpnData, 16 ); // mpnData always in little endian + return rStrm; +} + +BiffOutputStream& operator<<( BiffOutputStream& rStrm, const BiffGuid& rGuid ) +{ + rStrm.setPortionSize( 16 ); + rStrm.write( rGuid.mpnData, 16 ); // mpnData already in little endian + rStrm.setPortionSize( 0 ); + return rStrm; +} + +// ============================================================================ + +namespace { + +const sal_Int32 BIFF_RK_100FLAG = 0x00000001; +const sal_Int32 BIFF_RK_INTFLAG = 0x00000002; +const sal_Int32 BIFF_RK_VALUEMASK = 0xFFFFFFFC; + +// ---------------------------------------------------------------------------- + +static const struct CodePageEntry +{ + sal_uInt16 mnCodePage; + rtl_TextEncoding meTextEnc; +} +spCodePages[] = +{ + { 437, RTL_TEXTENCODING_IBM_437 }, // OEM US +// { 720, RTL_TEXTENCODING_IBM_720 }, // OEM Arabic + { 737, RTL_TEXTENCODING_IBM_737 }, // OEM Greek + { 775, RTL_TEXTENCODING_IBM_775 }, // OEM Baltic + { 850, RTL_TEXTENCODING_IBM_850 }, // OEM Latin I + { 852, RTL_TEXTENCODING_IBM_852 }, // OEM Latin II (Central European) + { 855, RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic + { 857, RTL_TEXTENCODING_IBM_857 }, // OEM Turkish +// { 858, RTL_TEXTENCODING_IBM_858 }, // OEM Multilingual Latin I with Euro + { 860, RTL_TEXTENCODING_IBM_860 }, // OEM Portugese + { 861, RTL_TEXTENCODING_IBM_861 }, // OEM Icelandic + { 862, RTL_TEXTENCODING_IBM_862 }, // OEM Hebrew + { 863, RTL_TEXTENCODING_IBM_863 }, // OEM Canadian (French) + { 864, RTL_TEXTENCODING_IBM_864 }, // OEM Arabic + { 865, RTL_TEXTENCODING_IBM_865 }, // OEM Nordic + { 866, RTL_TEXTENCODING_IBM_866 }, // OEM Cyrillic (Russian) + { 869, RTL_TEXTENCODING_IBM_869 }, // OEM Greek (Modern) + { 874, RTL_TEXTENCODING_MS_874 }, // MS Windows Thai + { 932, RTL_TEXTENCODING_MS_932 }, // MS Windows Japanese Shift-JIS + { 936, RTL_TEXTENCODING_MS_936 }, // MS Windows Chinese Simplified GBK + { 949, RTL_TEXTENCODING_MS_949 }, // MS Windows Korean (Wansung) + { 950, RTL_TEXTENCODING_MS_950 }, // MS Windows Chinese Traditional BIG5 + { 1200, RTL_TEXTENCODING_DONTKNOW }, // Unicode (BIFF8) - return *_DONTKNOW to preserve old code page + { 1250, RTL_TEXTENCODING_MS_1250 }, // MS Windows Latin II (Central European) + { 1251, RTL_TEXTENCODING_MS_1251 }, // MS Windows Cyrillic + { 1252, RTL_TEXTENCODING_MS_1252 }, // MS Windows Latin I (BIFF4-BIFF8) + { 1253, RTL_TEXTENCODING_MS_1253 }, // MS Windows Greek + { 1254, RTL_TEXTENCODING_MS_1254 }, // MS Windows Turkish + { 1255, RTL_TEXTENCODING_MS_1255 }, // MS Windows Hebrew + { 1256, RTL_TEXTENCODING_MS_1256 }, // MS Windows Arabic + { 1257, RTL_TEXTENCODING_MS_1257 }, // MS Windows Baltic + { 1258, RTL_TEXTENCODING_MS_1258 }, // MS Windows Vietnamese + { 1361, RTL_TEXTENCODING_MS_1361 }, // MS Windows Korean (Johab) + { 10000, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman + { 32768, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman + { 32769, RTL_TEXTENCODING_MS_1252 } // MS Windows Latin I (BIFF2-BIFF3) +}; + +/** Predicate to search by given code page. */ +struct CodePageEntry_CPPred +{ + inline explicit CodePageEntry_CPPred( sal_uInt16 nCodePage ) : mnCodePage( nCodePage ) {} + inline bool operator()( const CodePageEntry& rEntry ) const { return rEntry.mnCodePage == mnCodePage; } + sal_uInt16 mnCodePage; +}; + +/** Predicate to search by given text encoding. */ +struct CodePageEntry_TEPred +{ + inline explicit CodePageEntry_TEPred( rtl_TextEncoding eTextEnc ) : meTextEnc( eTextEnc ) {} + inline bool operator()( const CodePageEntry& rEntry ) const { return rEntry.meTextEnc == meTextEnc; } + rtl_TextEncoding meTextEnc; +}; + +} // namespace + +// ============================================================================ + +const BiffGuid BiffHelper::maGuidStdHlink( + 0x79EAC9D0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B ); + +const BiffGuid BiffHelper::maGuidUrlMoniker( + 0x79EAC9E0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B ); + +const BiffGuid BiffHelper::maGuidFileMoniker( + 0x00000303, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 ); + +// conversion ----------------------------------------------------------------- + +double BiffHelper::calcDoubleFromRk( sal_Int32 nRkValue ) +{ + double fValue = 0.0; + if( getFlag( nRkValue, BIFF_RK_INTFLAG ) ) + { + sal_Int32 nTemp = nRkValue >> 2; + setFlag< sal_Int32 >( nTemp, 0xE0000000, nRkValue < 0 ); + fValue = nTemp; + } + else + { + sal_math_Double* pDouble = reinterpret_cast< sal_math_Double* >( &fValue ); + pDouble->w32_parts.msw = static_cast< sal_uInt32 >( nRkValue & BIFF_RK_VALUEMASK ); + } + + if( getFlag( nRkValue, BIFF_RK_100FLAG ) ) + fValue /= 100.0; + + return fValue; +} + +namespace { + +bool lclCalcRkFromDouble( sal_Int32& ornRkValue, double fValue ) +{ + // double + const sal_math_Double* pValue = reinterpret_cast< const sal_math_Double* >( &fValue ); + if( (pValue->w32_parts.lsw == 0) && ((pValue->w32_parts.msw & 0x3) == 0) ) + { + ornRkValue = static_cast< sal_Int32 >( pValue->w32_parts.msw ); + return true; + } + + // integer + double fInt = 0.0; + double fFrac = modf( fValue, &fInt ); + if( (fFrac == 0.0) && (-536870912.0 <= fInt) && (fInt <= 536870911.0) ) // 2^29 + { + ornRkValue = static_cast< sal_Int32 >( fInt ); + ornRkValue <<= 2; + ornRkValue |= BIFF_RK_INTFLAG; + return true; + } + + return false; +} + +} // namespace + +bool BiffHelper::calcRkFromDouble( sal_Int32& ornRkValue, double fValue ) +{ + if( lclCalcRkFromDouble( ornRkValue, fValue ) ) + return true; + + if( lclCalcRkFromDouble( ornRkValue, fValue * 100 ) ) + { + ornRkValue |= BIFF_RK_100FLAG; + return true; + } + + return false; +} + +double BiffHelper::calcDoubleFromError( sal_uInt8 nErrorCode ) +{ + sal_uInt16 nApiError = 0x7FFF; + switch( nErrorCode ) + { + case BIFF_ERR_NULL: nApiError = 521; break; + case BIFF_ERR_DIV0: nApiError = 532; break; + case BIFF_ERR_VALUE: nApiError = 519; break; + case BIFF_ERR_REF: nApiError = 524; break; + case BIFF_ERR_NAME: nApiError = 525; break; + case BIFF_ERR_NUM: nApiError = 503; break; + case BIFF_ERR_NA: nApiError = 0x7FFF; break; + default: OSL_ENSURE( false, "BiffHelper::calcDoubleFromError - unknown error code" ); + } + double fValue; + ::rtl::math::setNan( &fValue ); + reinterpret_cast< sal_math_Double* >( &fValue )->nan_parts.fraction_lo = nApiError; + return fValue; +} + +rtl_TextEncoding BiffHelper::calcTextEncodingFromCodePage( sal_uInt16 nCodePage ) +{ + const CodePageEntry* pEntry = ::std::find_if( spCodePages, STATIC_ARRAY_END( spCodePages ), CodePageEntry_CPPred( nCodePage ) ); + if( pEntry == STATIC_ARRAY_END( spCodePages ) ) + { + OSL_ENSURE( false, "UnitConverter::calcTextEncodingFromCodePage - unknown code page" ); + return RTL_TEXTENCODING_DONTKNOW; + } + return pEntry->meTextEnc; +} + +sal_uInt16 BiffHelper::calcCodePageFromTextEncoding( rtl_TextEncoding eTextEnc ) +{ + const CodePageEntry* pEntry = ::std::find_if( spCodePages, STATIC_ARRAY_END( spCodePages ), CodePageEntry_TEPred( eTextEnc ) ); + if( pEntry == STATIC_ARRAY_END( spCodePages ) ) + { + OSL_ENSURE( false, "UnitConverter::calcCodePageFromTextEncoding - unsupported text encoding" ); + return 1252; + } + return pEntry->mnCodePage; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/biffinputstream.cxx b/oox/source/xls/biffinputstream.cxx new file mode 100644 index 000000000000..70428f9de3ad --- /dev/null +++ b/oox/source/xls/biffinputstream.cxx @@ -0,0 +1,646 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: biffinputstream.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/biffinputstream.hxx" +#include "oox/helper/binaryinputstream.hxx" + +using ::rtl::OUString; +using ::rtl::OString; +using ::rtl::OStringToOUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace prv { + +BiffInputRecordBuffer::BiffInputRecordBuffer( BinaryInputStream& rInStrm ) : + mrInStrm( rInStrm ), + mpCurrentData( 0 ), + mnHeaderPos( -1 ), + mnBodyPos( 0 ), + mnBufferBodyPos( 0 ), + mnNextHeaderPos( 0 ), + mnRecId( BIFF_ID_UNKNOWN ), + mnRecSize( 0 ), + mnRecPos( 0 ), + mbValidHeader( false ) +{ + OSL_ENSURE( mrInStrm.isSeekable(), "BiffInputRecordBuffer::BiffInputRecordBuffer - stream must be seekable" ); + mrInStrm.seekToStart(); + maOriginalData.reserve( SAL_MAX_UINT16 ); + maDecodedData.reserve( SAL_MAX_UINT16 ); + enableDecoder( false ); // updates mpCurrentData +} + +void BiffInputRecordBuffer::restartAt( sal_Int64 nPos ) +{ + mnHeaderPos = -1; + mnBodyPos = mnBufferBodyPos = 0; + mnNextHeaderPos = nPos; + mnRecId = BIFF_ID_UNKNOWN; + mnRecSize = mnRecPos = 0; + mbValidHeader = false; +} + +void BiffInputRecordBuffer::setDecoder( BiffDecoderRef xDecoder ) +{ + mxDecoder = xDecoder; + enableDecoder( true ); + updateDecoded(); +} + +void BiffInputRecordBuffer::enableDecoder( bool bEnable ) +{ + mpCurrentData = (bEnable && mxDecoder.get() && mxDecoder->isValid()) ? &maDecodedData : &maOriginalData; +} + +bool BiffInputRecordBuffer::startRecord( sal_Int64 nHeaderPos ) +{ + mbValidHeader = (0 <= nHeaderPos) && (nHeaderPos + 4 <= mrInStrm.getLength()); + if( mbValidHeader ) + { + mnHeaderPos = nHeaderPos; + mrInStrm.seek( nHeaderPos ); + mrInStrm >> mnRecId >> mnRecSize; + mnBodyPos = mrInStrm.tell(); + mnNextHeaderPos = mnBodyPos + mnRecSize; + mbValidHeader = mnNextHeaderPos <= mrInStrm.getLength(); + } + if( !mbValidHeader ) + { + mnHeaderPos = mnBodyPos = -1; + mnNextHeaderPos = 0; + mnRecId = BIFF_ID_UNKNOWN; + mnRecSize = 0; + } + mnRecPos = 0; + return mbValidHeader; +} + +bool BiffInputRecordBuffer::startNextRecord() +{ + return startRecord( mnNextHeaderPos ); +} + +sal_uInt16 BiffInputRecordBuffer::getNextRecId() +{ + sal_uInt16 nRecId = BIFF_ID_UNKNOWN; + if( mbValidHeader && (mnNextHeaderPos + 4 <= mrInStrm.getLength()) ) + { + mrInStrm.seek( mnNextHeaderPos ); + mrInStrm >> nRecId; + } + return nRecId; +} + +void BiffInputRecordBuffer::read( void* opData, sal_uInt16 nBytes ) +{ + updateBuffer(); + OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::read - nothing to read" ); + OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::read - buffer overflow" ); + memcpy( opData, &(*mpCurrentData)[ mnRecPos ], nBytes ); + mnRecPos = mnRecPos + nBytes; +} + +inline void BiffInputRecordBuffer::skip( sal_uInt16 nBytes ) +{ + OSL_ENSURE( nBytes > 0, "BiffInputRecordBuffer::skip - nothing to skip" ); + OSL_ENSURE( nBytes <= getRecLeft(), "BiffInputRecordBuffer::skip - buffer overflow" ); + mnRecPos = mnRecPos + nBytes; +} + +void BiffInputRecordBuffer::updateBuffer() +{ + OSL_ENSURE( mbValidHeader, "BiffInputRecordBuffer::updateBuffer - invalid access" ); + if( mnBodyPos != mnBufferBodyPos ) + { + mrInStrm.seek( mnBodyPos ); + maOriginalData.resize( mnRecSize ); + if( mnRecSize > 0 ) + mrInStrm.read( &maOriginalData.front(), static_cast< sal_Int32 >( mnRecSize ) ); + mnBufferBodyPos = mnBodyPos; + updateDecoded(); + } +} + +void BiffInputRecordBuffer::updateDecoded() +{ + if( mxDecoder.get() && mxDecoder->isValid() ) + { + maDecodedData.resize( mnRecSize ); + if( mnRecSize > 0 ) + mxDecoder->decode( &maDecodedData.front(), &maOriginalData.front(), mnBodyPos, mnRecSize ); + } +} + +} // namespace prv + +// ============================================================================ + +BiffInputStream::BiffInputStream( BinaryInputStream& rInStream, bool bContLookup ) : + maRecBuffer( rInStream ), + mnRecHandle( -1 ), + mnRecId( BIFF_ID_UNKNOWN ), + mnAltContId( BIFF_ID_UNKNOWN ), + mnCurrRecSize( 0 ), + mnComplRecSize( 0 ), + mbHasComplRec( false ), + mcNulSubst( BIFF_DEF_NUL_SUBST_CHAR ), + mbCont( bContLookup ), + mbValid( false ) +{ +} + +BiffInputStream::~BiffInputStream() +{ +} + +// record control ------------------------------------------------------------- + +bool BiffInputStream::startNextRecord() +{ + bool bValidRec = false; + /* #i4266# ignore zero records (id==len==0) (e.g. the application + "Crystal Report" writes zero records between other records) */ + bool bIsZeroRec = false; + do + { + // record header is never encrypted + maRecBuffer.enableDecoder( false ); + // read header of next raw record, returns false at end of stream + bValidRec = maRecBuffer.startNextRecord(); + // ignore record, if identifier and size are zero + bIsZeroRec = (maRecBuffer.getRecId() == 0) && (maRecBuffer.getRecSize() == 0); + } + while( bValidRec && ((mbCont && isContinueId( maRecBuffer.getRecId() )) || bIsZeroRec) ); + + // setup other class members + setupRecord(); + return isInRecord(); +} + +bool BiffInputStream::startRecordByHandle( sal_Int64 nRecHandle ) +{ + rewindToRecord( nRecHandle ); + return startNextRecord(); +} + +void BiffInputStream::resetRecord( bool bContLookup, sal_uInt16 nAltContId ) +{ + if( isInRecord() ) + { + mbCont = bContLookup; + mnAltContId = nAltContId; + restartRecord( true ); + maRecBuffer.enableDecoder( true ); + } +} + +void BiffInputStream::rewindRecord() +{ + rewindToRecord( mnRecHandle ); +} + +// decoder -------------------------------------------------------------------- + +void BiffInputStream::setDecoder( BiffDecoderRef xDecoder ) +{ + maRecBuffer.setDecoder( xDecoder ); +} + +BiffDecoderRef BiffInputStream::getDecoder() const +{ + return maRecBuffer.getDecoder(); +} + +void BiffInputStream::enableDecoder( bool bEnable ) +{ + maRecBuffer.enableDecoder( bEnable ); +} + +// stream/record state and info ----------------------------------------------- + +sal_uInt32 BiffInputStream::getRecPos() const +{ + return mbValid ? (mnCurrRecSize - maRecBuffer.getRecLeft()) : BIFF_REC_SEEK_TO_END; +} + +sal_uInt32 BiffInputStream::getRecSize() +{ + if( !mbHasComplRec ) + { + sal_uInt32 nCurrPos = getRecPos(); // save current position in record + while( jumpToNextContinue() ); // jumpToNextContinue() adds up mnCurrRecSize + mnComplRecSize = mnCurrRecSize; + mbHasComplRec = true; + seek( nCurrPos ); // restore position, seek() resets old mbValid state + } + return mnComplRecSize; +} + +sal_uInt32 BiffInputStream::getRecLeft() +{ + return mbValid ? (getRecSize() - getRecPos()) : 0; +} + +sal_uInt16 BiffInputStream::getNextRecId() +{ + sal_uInt16 nRecId = BIFF_ID_UNKNOWN; + if( isInRecord() ) + { + sal_uInt32 nCurrPos = getRecPos(); // save current position in record + while( jumpToNextContinue() ); // skip following CONTINUE records + if( maRecBuffer.startNextRecord() ) // read header of next record + nRecId = maRecBuffer.getRecId(); + seek( nCurrPos ); // restore position, seek() resets old mbValid state + } + return nRecId; +} + +sal_Int64 BiffInputStream::getCoreStreamPos() const +{ + return maRecBuffer.getCoreStream().tell(); +} + +sal_Int64 BiffInputStream::getCoreStreamSize() const +{ + return maRecBuffer.getCoreStream().getLength(); +} + +// stream read access --------------------------------------------------------- + +sal_uInt32 BiffInputStream::read( void* opData, sal_uInt32 nBytes ) +{ + sal_uInt32 nRet = 0; + if( mbValid && opData && (nBytes > 0) ) + { + sal_uInt8* pnBuffer = reinterpret_cast< sal_uInt8* >( opData ); + sal_uInt32 nBytesLeft = nBytes; + + while( mbValid && (nBytesLeft > 0) ) + { + sal_uInt16 nReadSize = getMaxRawReadSize( nBytesLeft ); + // check nReadSize, stream may already be located at end of a raw record + if( nReadSize > 0 ) + { + maRecBuffer.read( pnBuffer, nReadSize ); + nRet += nReadSize; + pnBuffer += nReadSize; + nBytesLeft -= nReadSize; + } + if( nBytesLeft > 0 ) + jumpToNextContinue(); + OSL_ENSURE( mbValid, "BiffInputStream::read - record overread" ); + } + } + return nRet; +} + +// seeking -------------------------------------------------------------------- + +BiffInputStream& BiffInputStream::seek( sal_uInt32 nRecPos ) +{ + if( isInRecord() ) + { + if( !mbValid || (nRecPos < getRecPos()) ) + restartRecord( false ); + if( mbValid && (nRecPos > getRecPos()) ) + skip( nRecPos - getRecPos() ); + } + return *this; +} + +BiffInputStream& BiffInputStream::skip( sal_uInt32 nBytes ) +{ + sal_uInt32 nBytesLeft = nBytes; + while( mbValid && (nBytesLeft > 0) ) + { + sal_uInt16 nSkipSize = getMaxRawReadSize( nBytesLeft ); + // check nSkipSize, stream may already be located at end of a raw record + if( nSkipSize > 0 ) + { + maRecBuffer.skip( nSkipSize ); + nBytesLeft -= nSkipSize; + } + if( nBytesLeft > 0 ) + jumpToNextContinue(); + OSL_ENSURE( mbValid, "BiffInputStream::skip - record overread" ); + } + return *this; +} + +// character arrays ----------------------------------------------------------- + +OString BiffInputStream::readCharArray( sal_uInt16 nChars ) +{ + ::std::vector< sal_Char > aBuffer( static_cast< size_t >( nChars ) + 1 ); + size_t nCharsRead = static_cast< size_t >( read( &aBuffer.front(), nChars ) ); + aBuffer[ nCharsRead ] = 0; + return OString( &aBuffer.front() ); +} + +OUString BiffInputStream::readCharArray( sal_uInt16 nChars, rtl_TextEncoding eTextEnc ) +{ + return OStringToOUString( readCharArray( nChars ), eTextEnc ); +} + +OUString BiffInputStream::readUnicodeArray( sal_uInt16 nChars ) +{ + ::std::vector< sal_Unicode > aBuffer; + aBuffer.reserve( static_cast< size_t >( nChars ) + 1 ); + sal_uInt16 nChar; + for( sal_uInt16 nCharIdx = 0; mbValid && (nCharIdx < nChars); ++nCharIdx ) + { + readValue( nChar ); + aBuffer.push_back( static_cast< sal_Unicode >( nChar ) ); + } + aBuffer.push_back( 0 ); + return OUString( &aBuffer.front() ); +} + +// byte strings --------------------------------------------------------------- + +OString BiffInputStream::readByteString( bool b16BitLen ) +{ + sal_uInt16 nStrLen = b16BitLen ? readuInt16() : readuInt8(); + return readCharArray( nStrLen ); +} + +OUString BiffInputStream::readByteString( bool b16BitLen, rtl_TextEncoding eTextEnc ) +{ + return OStringToOUString( readByteString( b16BitLen ), eTextEnc ); +} + +void BiffInputStream::skipByteString( bool b16BitLen ) +{ + skip( b16BitLen ? readuInt16() : readuInt8() ); +} + +// Unicode strings ------------------------------------------------------------ + +sal_uInt32 BiffInputStream::readExtendedUniStringHeader( + bool& rb16Bit, bool& rbFonts, bool& rbPhonetic, + sal_uInt16& rnFontCount, sal_uInt32& rnPhoneticSize, sal_uInt8 nFlags ) +{ + OSL_ENSURE( !getFlag( nFlags, BIFF_STRF_UNKNOWN ), "BiffInputStream::readExtendedUniStringHeader - unknown flags" ); + rb16Bit = getFlag( nFlags, BIFF_STRF_16BIT ); + rbFonts = getFlag( nFlags, BIFF_STRF_RICH ); + rbPhonetic = getFlag( nFlags, BIFF_STRF_PHONETIC ); + rnFontCount = 0; + if( rbFonts ) readValue( rnFontCount ); + rnPhoneticSize = 0; + if( rbPhonetic ) readValue( rnPhoneticSize ); + return rnPhoneticSize + 4 * rnFontCount; +} + +sal_uInt32 BiffInputStream::readExtendedUniStringHeader( bool& rb16Bit, sal_uInt8 nFlags ) +{ + bool bFonts, bPhonetic; + sal_uInt16 nFontCount; + sal_uInt32 nPhoneticSize; + return readExtendedUniStringHeader( rb16Bit, bFonts, bPhonetic, nFontCount, nPhoneticSize, nFlags ); +} + +OUString BiffInputStream::readRawUniString( sal_uInt16 nChars, bool b16Bit ) +{ + ::std::vector< sal_Unicode > aCharVec; + aCharVec.reserve( nChars + 1 ); + + /* This function has to react on CONTINUE records to reads the repeated + flags field, so readUnicodeArray() cannot be used here. */ + sal_uInt16 nCharsLeft = nChars; + while( isValid() && (nCharsLeft > 0) ) + { + sal_uInt16 nPortionCount = 0; + if( b16Bit ) + { + nPortionCount = ::std::min< sal_uInt16 >( nCharsLeft, maRecBuffer.getRecLeft() / 2 ); + OSL_ENSURE( (nPortionCount <= nCharsLeft) || ((maRecBuffer.getRecLeft() & 1) == 0), + "BiffInputStream::readRawUniString - missing a byte" ); + // read the character array + sal_uInt16 nReadChar; + for( sal_uInt16 nCharIdx = 0; isValid() && (nCharIdx < nPortionCount); ++nCharIdx ) + { + readValue( nReadChar ); + aCharVec.push_back( (nReadChar == 0) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar ) ); + } + } + else + { + nPortionCount = getMaxRawReadSize( nCharsLeft ); + // read the character array + sal_uInt8 nReadChar; + for( sal_uInt16 nCharIdx = 0; isValid() && (nCharIdx < nPortionCount); ++nCharIdx ) + { + readValue( nReadChar ); + aCharVec.push_back( (nReadChar == 0) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar ) ); + } + } + + // prepare for next CONTINUE record + nCharsLeft = nCharsLeft - nPortionCount; + if( nCharsLeft > 0 ) + jumpToNextStringContinue( b16Bit ); + } + + // string may contain embedded NUL characters, do not create the OUString by length of vector + aCharVec.push_back( 0 ); + return OUString( &aCharVec.front() ); +} + +OUString BiffInputStream::readUniString( sal_uInt16 nChars, sal_uInt8 nFlags ) +{ + bool b16Bit; + sal_uInt32 nExtSize = readExtendedUniStringHeader( b16Bit, nFlags ); + OUString aStr = readRawUniString( nChars, b16Bit ); + skip( nExtSize ); + return aStr; +} + +OUString BiffInputStream::readUniString( sal_uInt16 nChars ) +{ + return readUniString( nChars, readuInt8() ); +} + +OUString BiffInputStream::readUniString() +{ + return readUniString( readuInt16() ); +} + +void BiffInputStream::skipRawUniString( sal_uInt16 nChars, bool b16Bit ) +{ + sal_uInt16 nCharsLeft = nChars; + while( isValid() && (nCharsLeft > 0) ) + { + sal_uInt16 nPortionCount; + if( b16Bit ) + { + nPortionCount = ::std::min< sal_uInt16 >( nCharsLeft, maRecBuffer.getRecLeft() / 2 ); + OSL_ENSURE( (nPortionCount <= nCharsLeft) || ((maRecBuffer.getRecLeft() & 1) == 0), + "BiffInputStream::skipRawUniString - missing a byte" ); + skip( 2 * nPortionCount ); + } + else + { + nPortionCount = getMaxRawReadSize( nCharsLeft ); + skip( nPortionCount ); + } + + // prepare for next CONTINUE record + nCharsLeft = nCharsLeft - nPortionCount; + if( nCharsLeft > 0 ) + jumpToNextStringContinue( b16Bit ); + } +} + +void BiffInputStream::skipUniString( sal_uInt16 nChars, sal_uInt8 nFlags ) +{ + bool b16Bit; + sal_uInt32 nExtSize = readExtendedUniStringHeader( b16Bit, nFlags ); + skipRawUniString( nChars, b16Bit ); + skip( nExtSize ); +} + +void BiffInputStream::skipUniString( sal_uInt16 nChars ) +{ + skipUniString( nChars, readuInt8() ); +} + +void BiffInputStream::skipUniString() +{ + skipUniString( readuInt16() ); +} + +// private -------------------------------------------------------------------- + +void BiffInputStream::setupRecord() +{ + // initialize class members + mnRecHandle = maRecBuffer.getRecHeaderPos(); + mnRecId = maRecBuffer.getRecId(); + mnAltContId = BIFF_ID_UNKNOWN; + mnCurrRecSize = mnComplRecSize = maRecBuffer.getRecSize(); + mbHasComplRec = !mbCont; + mbValid = isInRecord(); + setNulSubstChar( BIFF_DEF_NUL_SUBST_CHAR ); + // enable decoder in new record + enableDecoder( true ); +} + +void BiffInputStream::restartRecord( bool bInvalidateRecSize ) +{ + if( isInRecord() ) + { + maRecBuffer.startRecord( getRecHandle() ); + mnCurrRecSize = maRecBuffer.getRecSize(); + if( bInvalidateRecSize ) + { + mnComplRecSize = mnCurrRecSize; + mbHasComplRec = !mbCont; + } + mbValid = true; + } +} + +void BiffInputStream::rewindToRecord( sal_Int64 nRecHandle ) +{ + if( nRecHandle >= 0 ) + { + maRecBuffer.restartAt( nRecHandle ); + mnRecHandle = -1; + mbValid = false; + } +} + +bool BiffInputStream::isContinueId( sal_uInt16 nRecId ) const +{ + return (nRecId == BIFF_ID_CONT) || (nRecId == mnAltContId); +} + +bool BiffInputStream::jumpToNextContinue() +{ + mbValid = mbValid && mbCont && isContinueId( maRecBuffer.getNextRecId() ) && maRecBuffer.startNextRecord(); + if( mbValid ) + mnCurrRecSize += maRecBuffer.getRecSize(); + return mbValid; +} + +bool BiffInputStream::jumpToNextStringContinue( bool& rb16Bit ) +{ + OSL_ENSURE( maRecBuffer.getRecLeft() == 0, "BiffInputStream::jumpToNextStringContinue - unexpected garbage" ); + + if( mbCont && (getRecLeft() > 0) ) + { + jumpToNextContinue(); + } + else if( mnRecId == BIFF_ID_CONT ) + { + /* CONTINUE handling is off, but we have started reading in a CONTINUE + record -> start next CONTINUE for TXO import. We really start a new + record here - no chance to return to string origin. */ + mbValid = mbValid && (maRecBuffer.getNextRecId() == BIFF_ID_CONT) && maRecBuffer.startNextRecord(); + if( mbValid ) + setupRecord(); + } + + // trying to read the flags invalidates stream, if no CONTINUE record has been found + sal_uInt8 nFlags; + readValue( nFlags ); + rb16Bit = getFlag( nFlags, BIFF_STRF_16BIT ); + return mbValid; +} + +bool BiffInputStream::ensureRawReadSize( sal_uInt16 nBytes ) +{ + if( mbValid && (nBytes > 0) ) + { + while( mbValid && (maRecBuffer.getRecLeft() == 0) ) jumpToNextContinue(); + mbValid = mbValid && (nBytes <= maRecBuffer.getRecLeft()); + OSL_ENSURE( mbValid, "BiffInputStream::ensureRawReadSize - record overread" ); + } + return mbValid; +} + +sal_uInt16 BiffInputStream::getMaxRawReadSize( sal_uInt32 nBytes ) const +{ + return static_cast< sal_uInt16 >( ::std::min< sal_uInt32 >( nBytes, maRecBuffer.getRecLeft() ) ); +} +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/biffoutputstream.cxx b/oox/source/xls/biffoutputstream.cxx new file mode 100644 index 000000000000..570b145931ce --- /dev/null +++ b/oox/source/xls/biffoutputstream.cxx @@ -0,0 +1,191 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: biffoutputstream.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/biffoutputstream.hxx" +#include "oox/helper/binaryoutputstream.hxx" + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace prv { + +BiffOutputRecordBuffer::BiffOutputRecordBuffer( BinaryOutputStream& rOutStrm, sal_uInt16 nMaxRecSize ) : + mrOutStrm( rOutStrm ), + mnMaxRecSize( nMaxRecSize ), + mnRecId( BIFF_ID_UNKNOWN ), + mbInRec( false ) +{ + OSL_ENSURE( mrOutStrm.isSeekable(), "BiffOutputRecordBuffer::BiffOutputRecordBuffer - stream must be seekable" ); + maData.reserve( SAL_MAX_UINT16 ); +} + +void BiffOutputRecordBuffer::startRecord( sal_uInt16 nRecId ) +{ + OSL_ENSURE( !mbInRec, "BiffOutputRecordBuffer::startRecord - another record still open" ); + mnRecId = nRecId; + maData.clear(); + mbInRec = true; +} + +void BiffOutputRecordBuffer::endRecord() +{ + OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::endRecord - no record open" ); + sal_uInt16 nRecSize = getLimitedValue< sal_uInt16, size_t >( maData.size(), 0, SAL_MAX_UINT16 ); + mrOutStrm.seekToEnd(); + mrOutStrm << mnRecId << nRecSize; + if( nRecSize > 0 ) + mrOutStrm.write( &maData.front(), nRecSize ); + mbInRec = false; +} + +void BiffOutputRecordBuffer::write( const void* pData, sal_uInt16 nBytes ) +{ + OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" ); + OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" ); + OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" ); + maData.resize( maData.size() + nBytes ); + memcpy( &*(maData.end() - nBytes), pData, nBytes ); +} + +void BiffOutputRecordBuffer::fill( sal_uInt8 nValue, sal_uInt16 nBytes ) +{ + OSL_ENSURE( mbInRec, "BiffOutputRecordBuffer::write - no record open" ); + OSL_ENSURE( nBytes > 0, "BiffOutputRecordBuffer::write - nothing to write" ); + OSL_ENSURE( nBytes <= getRecLeft(), "BiffOutputRecordBuffer::write - buffer overflow" ); + maData.resize( maData.size() + nBytes, nValue ); +} + +} // namespace prv + +// ============================================================================ + +BiffOutputStream::BiffOutputStream( BinaryOutputStream& rOutStream, sal_uInt16 nMaxRecSize ) : + maRecBuffer( rOutStream, nMaxRecSize ), + mnPortionSize( 0 ), + mnPortionPos( 0 ) +{ +} + +BiffOutputStream::~BiffOutputStream() +{ +} + +// record control ------------------------------------------------------------- + +void BiffOutputStream::startRecord( sal_uInt16 nRecId ) +{ + maRecBuffer.startRecord( nRecId ); + setPortionSize( 0 ); +} + +void BiffOutputStream::endRecord() +{ + maRecBuffer.endRecord(); +} + +void BiffOutputStream::setPortionSize( sal_uInt16 nSize ) +{ + mnPortionSize = nSize; + mnPortionPos = 0; +} + +// stream/record state and info ----------------------------------------------- + +// stream write access -------------------------------------------------------- + +void BiffOutputStream::write( const void* pData, sal_uInt32 nBytes ) +{ + if( pData && (nBytes > 0) ) + { + const sal_uInt8* pnBuffer = reinterpret_cast< const sal_uInt8* >( pData ); + sal_uInt32 nBytesLeft = nBytes; + while( nBytesLeft > 0 ) + { + sal_uInt16 nBlockSize = prepareRawBlock( nBytesLeft ); + maRecBuffer.write( pnBuffer, nBlockSize ); + pnBuffer += nBlockSize; + nBytesLeft -= nBlockSize; + } + } +} + +void BiffOutputStream::fill( sal_uInt8 nValue, sal_uInt32 nBytes ) +{ + sal_uInt32 nBytesLeft = nBytes; + while( nBytesLeft > 0 ) + { + sal_uInt16 nBlockSize = prepareRawBlock( nBytesLeft ); + maRecBuffer.fill( nValue, nBlockSize ); + nBytesLeft -= nBlockSize; + } +} + +// private -------------------------------------------------------------------- + +void BiffOutputStream::ensureRawBlock( sal_uInt16 nSize ) +{ + if( (maRecBuffer.getRecLeft() < nSize) || + ((mnPortionSize > 0) && (mnPortionPos == 0) && (maRecBuffer.getRecLeft() < mnPortionSize)) ) + { + maRecBuffer.endRecord(); + maRecBuffer.startRecord( BIFF_ID_CONT ); + } + if( mnPortionSize > 0 ) + { + OSL_ENSURE( mnPortionPos + nSize <= mnPortionSize, "BiffOutputStreamI::ensureRawBlock - portion overflow" ); + mnPortionPos = (mnPortionPos + nSize) % mnPortionSize; // prevent compiler warning, do not use operator+=, operator%= + } +} + +sal_uInt16 BiffOutputStream::prepareRawBlock( sal_uInt32 nTotalSize ) +{ + sal_uInt16 nRecLeft = maRecBuffer.getRecLeft(); + if( mnPortionSize > 0 ) + { + OSL_ENSURE( mnPortionPos == 0, "BiffOutputStream::prepareRawBlock - block operation inside portion" ); + OSL_ENSURE( nTotalSize % mnPortionSize == 0, "BiffOutputStream::prepareRawBlock - portion size does not match block size" ); + nRecLeft = (nRecLeft / mnPortionSize) * mnPortionSize; + } + sal_uInt16 nSize = getLimitedValue< sal_uInt16, sal_uInt32 >( nTotalSize, 0, nRecLeft ); + ensureRawBlock( nSize ); + return nSize; +} +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/condformatbuffer.cxx b/oox/source/xls/condformatbuffer.cxx new file mode 100644 index 000000000000..f22910846d6c --- /dev/null +++ b/oox/source/xls/condformatbuffer.cxx @@ -0,0 +1,782 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: condformatbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/condformatbuffer.hxx" +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/sheet/ConditionOperator.hpp> +#include <com/sun/star/sheet/XSheetConditionalEntries.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheets.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/stylesbuffer.hxx" +#include "oox/xls/validationpropertyhelper.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::beans::PropertyValue; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::style::XStyleFamiliesSupplier; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XNameContainer; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::sheet::ConditionOperator; +using ::com::sun::star::table::XCellRange; +using ::com::sun::star::sheet::XSheetCellRanges; +using ::com::sun::star::sheet::XSheetConditionalEntries; +using ::com::sun::star::sheet::XSpreadsheetDocument; +using ::com::sun::star::sheet::XSpreadsheets; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::style::XStyle; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_Int32 OOBIN_CFRULE_TYPE_CELLIS = 1; +const sal_Int32 OOBIN_CFRULE_TYPE_EXPRESSION = 2; +const sal_Int32 OOBIN_CFRULE_TYPE_COLORSCALE = 3; +const sal_Int32 OOBIN_CFRULE_TYPE_DATABAR = 4; +const sal_Int32 OOBIN_CFRULE_TYPE_TOPTEN = 5; +const sal_Int32 OOBIN_CFRULE_TYPE_ICONSET = 6; + +const sal_Int32 OOBIN_CFRULE_SUB_CELLIS = 0; +const sal_Int32 OOBIN_CFRULE_SUB_EXPRESSION = 1; +const sal_Int32 OOBIN_CFRULE_SUB_COLORSCALE = 2; +const sal_Int32 OOBIN_CFRULE_SUB_DATABAR = 3; +const sal_Int32 OOBIN_CFRULE_SUB_ICONSET = 4; +const sal_Int32 OOBIN_CFRULE_SUB_TOPTEN = 5; +const sal_Int32 OOBIN_CFRULE_SUB_UNIQUE = 7; +const sal_Int32 OOBIN_CFRULE_SUB_TEXT = 8; +const sal_Int32 OOBIN_CFRULE_SUB_BLANK = 9; +const sal_Int32 OOBIN_CFRULE_SUB_NOTBLANK = 10; +const sal_Int32 OOBIN_CFRULE_SUB_ERROR = 11; +const sal_Int32 OOBIN_CFRULE_SUB_NOTERROR = 12; +const sal_Int32 OOBIN_CFRULE_SUB_TODAY = 15; +const sal_Int32 OOBIN_CFRULE_SUB_TOMORROW = 16; +const sal_Int32 OOBIN_CFRULE_SUB_YESTERDAY = 17; +const sal_Int32 OOBIN_CFRULE_SUB_LAST7DAYS = 18; +const sal_Int32 OOBIN_CFRULE_SUB_LASTMONTH = 19; +const sal_Int32 OOBIN_CFRULE_SUB_NEXTMONTH = 20; +const sal_Int32 OOBIN_CFRULE_SUB_THISWEEK = 21; +const sal_Int32 OOBIN_CFRULE_SUB_NEXTWEEK = 22; +const sal_Int32 OOBIN_CFRULE_SUB_LASTWEEK = 23; +const sal_Int32 OOBIN_CFRULE_SUB_THISMONTH = 24; +const sal_Int32 OOBIN_CFRULE_SUB_ABOVEAVERAGE = 25; +const sal_Int32 OOBIN_CFRULE_SUB_BELOWAVERAGE = 26; +const sal_Int32 OOBIN_CFRULE_SUB_DUPLICATE = 27; +const sal_Int32 OOBIN_CFRULE_SUB_EQABOVEAVERAGE = 29; +const sal_Int32 OOBIN_CFRULE_SUB_EQBELOWAVERAGE = 30; + +const sal_Int32 OOBIN_CFRULE_TIMEOP_TODAY = 0; +const sal_Int32 OOBIN_CFRULE_TIMEOP_YESTERDAY = 1; +const sal_Int32 OOBIN_CFRULE_TIMEOP_LAST7DAYS = 2; +const sal_Int32 OOBIN_CFRULE_TIMEOP_THISWEEK = 3; +const sal_Int32 OOBIN_CFRULE_TIMEOP_LASTWEEK = 4; +const sal_Int32 OOBIN_CFRULE_TIMEOP_LASTMONTH = 5; +const sal_Int32 OOBIN_CFRULE_TIMEOP_TOMORROW = 6; +const sal_Int32 OOBIN_CFRULE_TIMEOP_NEXTWEEK = 7; +const sal_Int32 OOBIN_CFRULE_TIMEOP_NEXTMONTH = 8; +const sal_Int32 OOBIN_CFRULE_TIMEOP_THISMONTH = 9; + +const sal_uInt16 OOBIN_CFRULE_STOPIFTRUE = 0x0002; +const sal_uInt16 OOBIN_CFRULE_ABOVEAVERAGE = 0x0004; +const sal_uInt16 OOBIN_CFRULE_BOTTOM = 0x0008; +const sal_uInt16 OOBIN_CFRULE_PERCENT = 0x0010; + +// ---------------------------------------------------------------------------- + +template< typename Type > +void lclAppendProperty( ::std::vector< PropertyValue >& orProps, const OUString& rPropName, const Type& rValue ) +{ + orProps.push_back( PropertyValue() ); + orProps.back().Name = rPropName; + orProps.back().Value <<= rValue; +} + +} // namespace + +// ============================================================================ + +OoxCondFormatRuleData::OoxCondFormatRuleData() : + mnPriority( -1 ), + mnType( XML_TOKEN_INVALID ), + mnOperator( XML_TOKEN_INVALID ), + mnTimePeriod( XML_TOKEN_INVALID ), + mnRank( 0 ), + mnStdDev( 0 ), + mnDxfId( -1 ), + mbStopIfTrue( false ), + mbBottom( false ), + mbPercent( false ), + mbAboveAverage( true ), + mbEqualAverage( false ) +{ +} + +void OoxCondFormatRuleData::setBinOperator( sal_Int32 nOperator ) +{ + static const sal_Int32 spnOperators[] = { + XML_TOKEN_INVALID, XML_between, XML_notBetween, XML_equal, XML_notEqual, + XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual }; + mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID ); +} + +void OoxCondFormatRuleData::setOobTextType( sal_Int32 nOperator ) +{ + // note: type XML_notContainsText vs. operator XML_notContains + static const sal_Int32 spnTypes[] = { XML_containsText, XML_notContainsText, XML_beginsWith, XML_endsWith }; + mnType = STATIC_ARRAY_SELECT( spnTypes, nOperator, XML_TOKEN_INVALID ); + static const sal_Int32 spnOperators[] = { XML_containsText, XML_notContains, XML_beginsWith, XML_endsWith }; + mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID ); +} + +// ============================================================================ + +CondFormatRule::CondFormatRule( const CondFormat& rCondFormat ) : + WorksheetHelper( rCondFormat ), + mrCondFormat( rCondFormat ) +{ +} + +void CondFormatRule::importCfRule( const AttributeList& rAttribs ) +{ + maOoxData.maText = rAttribs.getString( XML_text ); + maOoxData.mnPriority = rAttribs.getInteger( XML_priority, -1 ); + maOoxData.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID ); + maOoxData.mnOperator = rAttribs.getToken( XML_operator, XML_TOKEN_INVALID ); + maOoxData.mnTimePeriod = rAttribs.getToken( XML_timePeriod, XML_TOKEN_INVALID ); + maOoxData.mnRank = rAttribs.getInteger( XML_rank, 0 ); + maOoxData.mnStdDev = rAttribs.getInteger( XML_stdDev, 0 ); + maOoxData.mnDxfId = rAttribs.getInteger( XML_dxfId, -1 ); + maOoxData.mbStopIfTrue = rAttribs.getBool( XML_stopIfTrue, false ); + maOoxData.mbBottom = rAttribs.getBool( XML_bottom, false ); + maOoxData.mbPercent = rAttribs.getBool( XML_percent, false ); + maOoxData.mbAboveAverage = rAttribs.getBool( XML_aboveAverage, true ); + maOoxData.mbEqualAverage = rAttribs.getBool( XML_equalAverage, false ); +} + +void CondFormatRule::appendFormula( const OUString& rFormula ) +{ + TokensFormulaContext aContext( true, false ); + aContext.setBaseAddress( mrCondFormat.getRanges().getBaseAddress() ); + getFormulaParser().importFormula( aContext, rFormula ); + maOoxData.maFormulas.push_back( aContext ); +} + +void CondFormatRule::importCfRule( RecordInputStream& rStrm ) +{ + sal_Int32 nType, nSubType, nOperator, nFmla1Size, nFmla2Size, nFmla3Size; + sal_uInt16 nFlags; + rStrm >> nType >> nSubType >> maOoxData.mnDxfId >> maOoxData.mnPriority >> nOperator; + rStrm.skip( 8 ); + rStrm >> nFlags >> nFmla1Size >> nFmla2Size >> nFmla3Size >> maOoxData.maText; + + /* Import the formulas. For no obvious reason, the sizes of the formulas + are already stored before. Nevertheless the following formulas contain + their own sizes. */ + + // first formula + OSL_ENSURE( (nFmla1Size >= 0) || ((nFmla2Size == 0) && (nFmla3Size == 0)), "CondFormatRule::importCfRule - missing first formula" ); + OSL_ENSURE( (nFmla1Size > 0) == (rStrm.getRecLeft() >= 8), "CondFormatRule::importCfRule - formula size mismatch" ); + if( rStrm.getRecLeft() >= 8 ) + { + TokensFormulaContext aContext( true, false ); + aContext.setBaseAddress( mrCondFormat.getRanges().getBaseAddress() ); + getFormulaParser().importFormula( aContext, rStrm ); + maOoxData.maFormulas.push_back( aContext ); + + // second formula + OSL_ENSURE( (nFmla2Size >= 0) || (nFmla3Size == 0), "CondFormatRule::importCfRule - missing second formula" ); + OSL_ENSURE( (nFmla2Size > 0) == (rStrm.getRecLeft() >= 8), "CondFormatRule::importCfRule - formula size mismatch" ); + if( rStrm.getRecLeft() >= 8 ) + { + getFormulaParser().importFormula( aContext, rStrm ); + maOoxData.maFormulas.push_back( aContext ); + + // third formula + OSL_ENSURE( (nFmla3Size > 0) == (rStrm.getRecLeft() >= 8), "CondFormatRule::importCfRule - formula size mismatch" ); + if( rStrm.getRecLeft() >= 8 ) + { + getFormulaParser().importFormula( aContext, rStrm ); + maOoxData.maFormulas.push_back( aContext ); + } + } + } + + // flags + maOoxData.mbStopIfTrue = getFlag( nFlags, OOBIN_CFRULE_STOPIFTRUE ); + maOoxData.mbBottom = getFlag( nFlags, OOBIN_CFRULE_BOTTOM ); + maOoxData.mbPercent = getFlag( nFlags, OOBIN_CFRULE_PERCENT ); + maOoxData.mbAboveAverage = getFlag( nFlags, OOBIN_CFRULE_ABOVEAVERAGE ); + // no flag for equalAverage, must be determined from subtype below... + + // Convert the type/operator settings. This is a real mess... + switch( nType ) + { + case OOBIN_CFRULE_TYPE_CELLIS: + OSL_ENSURE( nSubType == OOBIN_CFRULE_SUB_CELLIS, "CondFormatRule::importCfRule - rule type/subtype mismatch" ); + maOoxData.mnType = XML_cellIs; + maOoxData.setBinOperator( nOperator ); + OSL_ENSURE( maOoxData.mnOperator != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unknown operator" ); + break; + case OOBIN_CFRULE_TYPE_EXPRESSION: + // here we have to look at the subtype to find the real type... + switch( nSubType ) + { + case OOBIN_CFRULE_SUB_EXPRESSION: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_expression; + break; + case OOBIN_CFRULE_SUB_UNIQUE: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_uniqueValues; + break; + case OOBIN_CFRULE_SUB_TEXT: + maOoxData.setOobTextType( nOperator ); + OSL_ENSURE( maOoxData.mnType != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unexpected operator value" ); + break; + case OOBIN_CFRULE_SUB_BLANK: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_containsBlanks; + break; + case OOBIN_CFRULE_SUB_NOTBLANK: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_notContainsBlanks; + break; + case OOBIN_CFRULE_SUB_ERROR: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_containsErrors; + break; + case OOBIN_CFRULE_SUB_NOTERROR: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_notContainsErrors; + break; + case OOBIN_CFRULE_SUB_TODAY: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_TODAY, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_today; + break; + case OOBIN_CFRULE_SUB_TOMORROW: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_TOMORROW, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_tomorrow; + break; + case OOBIN_CFRULE_SUB_YESTERDAY: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_YESTERDAY, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_yesterday; + break; + case OOBIN_CFRULE_SUB_LAST7DAYS: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_LAST7DAYS, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_last7Days; + break; + case OOBIN_CFRULE_SUB_LASTMONTH: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_LASTMONTH, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_lastMonth; + break; + case OOBIN_CFRULE_SUB_NEXTMONTH: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_NEXTMONTH, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_nextMonth; + break; + case OOBIN_CFRULE_SUB_THISWEEK: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_THISWEEK, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_thisWeek; + break; + case OOBIN_CFRULE_SUB_NEXTWEEK: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_NEXTWEEK, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_nextWeek; + break; + case OOBIN_CFRULE_SUB_LASTWEEK: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_LASTWEEK, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_lastWeek; + break; + case OOBIN_CFRULE_SUB_THISMONTH: + OSL_ENSURE( nOperator == OOBIN_CFRULE_TIMEOP_THISMONTH, "CondFormatRule::importCfRule - unexpected time operator value" ); + maOoxData.mnType = XML_timePeriod; + maOoxData.mnTimePeriod = XML_thisMonth; + break; + case OOBIN_CFRULE_SUB_ABOVEAVERAGE: + OSL_ENSURE( maOoxData.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" ); + maOoxData.mnType = XML_aboveAverage; + maOoxData.mnStdDev = nOperator; // operator field used for standard deviation + maOoxData.mbAboveAverage = true; + maOoxData.mbEqualAverage = false; // does not exist as real flag... + break; + case OOBIN_CFRULE_SUB_BELOWAVERAGE: + OSL_ENSURE( !maOoxData.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" ); + maOoxData.mnType = XML_aboveAverage; + maOoxData.mnStdDev = nOperator; // operator field used for standard deviation + maOoxData.mbAboveAverage = false; + maOoxData.mbEqualAverage = false; // does not exist as real flag... + break; + case OOBIN_CFRULE_SUB_DUPLICATE: + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_duplicateValues; + break; + case OOBIN_CFRULE_SUB_EQABOVEAVERAGE: + OSL_ENSURE( maOoxData.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" ); + maOoxData.mnType = XML_aboveAverage; + maOoxData.mnStdDev = nOperator; // operator field used for standard deviation + maOoxData.mbAboveAverage = true; + maOoxData.mbEqualAverage = true; // does not exist as real flag... + break; + case OOBIN_CFRULE_SUB_EQBELOWAVERAGE: + OSL_ENSURE( !maOoxData.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" ); + maOoxData.mnType = XML_aboveAverage; + maOoxData.mnStdDev = nOperator; // operator field used for standard deviation + maOoxData.mbAboveAverage = false; + maOoxData.mbEqualAverage = true; // does not exist as real flag... + break; + } + break; + case OOBIN_CFRULE_TYPE_COLORSCALE: + OSL_ENSURE( nSubType == OOBIN_CFRULE_SUB_COLORSCALE, "CondFormatRule::importCfRule - rule type/subtype mismatch" ); + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_colorScale; + break; + case OOBIN_CFRULE_TYPE_DATABAR: + OSL_ENSURE( nSubType == OOBIN_CFRULE_SUB_DATABAR, "CondFormatRule::importCfRule - rule type/subtype mismatch" ); + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_dataBar; + break; + case OOBIN_CFRULE_TYPE_TOPTEN: + OSL_ENSURE( nSubType == OOBIN_CFRULE_SUB_TOPTEN, "CondFormatRule::importCfRule - rule type/subtype mismatch" ); + maOoxData.mnType = XML_top10; + maOoxData.mnRank = nOperator; // operator field used for rank value + break; + case OOBIN_CFRULE_TYPE_ICONSET: + OSL_ENSURE( nSubType == OOBIN_CFRULE_SUB_ICONSET, "CondFormatRule::importCfRule - rule type/subtype mismatch" ); + OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" ); + maOoxData.mnType = XML_iconSet; + break; + default: + OSL_ENSURE( false, "CondFormatRule::importCfRule - unknown rule type" ); + } +} + +void CondFormatRule::importCfRule( BiffInputStream& rStrm, sal_Int32 nPriority ) +{ + sal_uInt8 nType, nOperator; + sal_uInt16 nFmla1Size, nFmla2Size; + sal_uInt32 nFlags; + rStrm >> nType >> nOperator >> nFmla1Size >> nFmla2Size >> nFlags; + rStrm.skip( 2 ); + + static const sal_Int32 spnTypeIds[] = { XML_TOKEN_INVALID, XML_cellIs, XML_expression }; + maOoxData.mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_TOKEN_INVALID ); + + maOoxData.setBinOperator( nOperator ); + maOoxData.mnPriority = nPriority; + maOoxData.mbStopIfTrue = true; + + DxfRef xDxf = getStyles().createDxf( &maOoxData.mnDxfId ); + xDxf->importCfRule( rStrm, nFlags ); + xDxf->finalizeImport(); + + // import the formulas + OSL_ENSURE( (nFmla1Size > 0) || (nFmla2Size == 0), "CondFormatRule::importCfRule - missing first formula" ); + if( nFmla1Size > 0 ) + { + TokensFormulaContext aContext( true, false ); + aContext.setBaseAddress( mrCondFormat.getRanges().getBaseAddress() ); + getFormulaParser().importFormula( aContext, rStrm, &nFmla1Size ); + maOoxData.maFormulas.push_back( aContext ); + if( nFmla2Size > 0 ) + { + getFormulaParser().importFormula( aContext, rStrm, &nFmla2Size ); + maOoxData.maFormulas.push_back( aContext ); + } + } +} + +void CondFormatRule::finalizeImport( const Reference< XSheetConditionalEntries >& rxEntries ) +{ + ConditionOperator eOperator = ::com::sun::star::sheet::ConditionOperator_NONE; + + /* Replacement formula for unsupported rule types (text comparison rules, + time period rules, cell type rules). The replacement formulas below may + contain several placeholders: + - '#B' will be replaced by the current base address (may occur + several times). + - '#R' will be replaced by the entire range list of the conditional + formatting (absolute addresses). + - '#T' will be replaced by the quoted comparison text. + - '#L' will be replaced by the length of the comparison text (from + the 'text' attribute) used in text comparison rules. + - '#K' will be replaced by the rank (from the 'rank' attribute) used in + top-10 rules. + - '#M' will be replaced by the top/bottom flag (from the 'bottom' + attribute) used in the RANK function in top-10 rules. + */ + OUString aReplaceFormula; + + switch( maOoxData.mnType ) + { + case XML_cellIs: + eOperator = ValidationPropertyHelper::convertToApiOperator( maOoxData.mnOperator ); + break; + case XML_expression: + eOperator = ::com::sun::star::sheet::ConditionOperator_FORMULA; + break; + case XML_containsText: + OSL_ENSURE( maOoxData.mnOperator == XML_containsText, "CondFormatRule::finalizeImport - unexpected operator" ); + aReplaceFormula = CREATE_OUSTRING( "NOT(ISERROR(SEARCH(#T,#B)))" ); + break; + case XML_notContainsText: + // note: type XML_notContainsText vs. operator XML_notContains + OSL_ENSURE( maOoxData.mnOperator == XML_notContains, "CondFormatRule::finalizeImport - unexpected operator" ); + aReplaceFormula = CREATE_OUSTRING( "ISERROR(SEARCH(#T,#B))" ); + break; + case XML_beginsWith: + OSL_ENSURE( maOoxData.mnOperator == XML_beginsWith, "CondFormatRule::finalizeImport - unexpected operator" ); + aReplaceFormula = CREATE_OUSTRING( "LEFT(#B,#L)=#T" ); + break; + case XML_endsWith: + OSL_ENSURE( maOoxData.mnOperator == XML_endsWith, "CondFormatRule::finalizeImport - unexpected operator" ); + aReplaceFormula = CREATE_OUSTRING( "RIGHT(#B,#L)=#T" ); + break; + case XML_timePeriod: + switch( maOoxData.mnTimePeriod ) + { + case XML_yesterday: + aReplaceFormula = CREATE_OUSTRING( "FLOOR(#B,1)=TODAY()-1" ); + break; + case XML_today: + aReplaceFormula = CREATE_OUSTRING( "FLOOR(#B,1)=TODAY()" ); + break; + case XML_tomorrow: + aReplaceFormula = CREATE_OUSTRING( "FLOOR(#B,1)=TODAY()+1" ); + break; + case XML_last7Days: + aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-7<FLOOR(#B,1),FLOOR(#B,1)<=TODAY())" ); + break; + case XML_lastWeek: + aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-WEEKDAY(TODAY())-7<FLOOR(#B,1),FLOOR(#B,1)<=TODAY()-WEEKDAY(TODAY()))" ); + break; + case XML_thisWeek: + aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-WEEKDAY(TODAY())<FLOOR(#B,1),FLOOR(#B,1)<=TODAY()-WEEKDAY(TODAY())+7)" ); + break; + case XML_nextWeek: + aReplaceFormula = CREATE_OUSTRING( "AND(TODAY()-WEEKDAY(TODAY())+7<FLOOR(#B,1),FLOOR(#B,1)<=TODAY()-WEEKDAY(TODAY())+14)" ); + break; + case XML_lastMonth: + aReplaceFormula = CREATE_OUSTRING( "OR(AND(MONTH(#B)=MONTH(TODAY())-1,YEAR(#B)=YEAR(TODAY())),AND(MONTH(#B)=12,MONTH(TODAY())=1,YEAR(#B)=YEAR(TODAY())-1))" ); + break; + case XML_thisMonth: + aReplaceFormula = CREATE_OUSTRING( "AND(MONTH(#B)=MONTH(TODAY()),YEAR(#B)=YEAR(TODAY()))" ); + break; + case XML_nextMonth: + aReplaceFormula = CREATE_OUSTRING( "OR(AND(MONTH(#B)=MONTH(TODAY())+1,YEAR(#B)=YEAR(TODAY())),AND(MONTH(#B)=1,MONTH(TODAY())=12,YEAR(#B)=YEAR(TODAY())+1))" ); + break; + default: + OSL_ENSURE( false, "CondFormatRule::finalizeImport - unknown time period type" ); + } + break; + case XML_containsBlanks: + aReplaceFormula = CREATE_OUSTRING( "LEN(TRIM(#B))=0" ); + break; + case XML_notContainsBlanks: + aReplaceFormula = CREATE_OUSTRING( "LEN(TRIM(#B))>0" ); + break; + case XML_containsErrors: + aReplaceFormula = CREATE_OUSTRING( "ISERROR(#B)" ); + break; + case XML_notContainsErrors: + aReplaceFormula = CREATE_OUSTRING( "NOT(ISERROR(#B))" ); + break; + case XML_top10: + if( maOoxData.mbPercent ) + aReplaceFormula = CREATE_OUSTRING( "RANK(#B,#R,#M)/COUNT(#R)<=#K%" ); + else + aReplaceFormula = CREATE_OUSTRING( "RANK(#B,#R,#M)<=#K" ); + break; + case XML_aboveAverage: + if( maOoxData.mnStdDev == 0 ) + { + if( maOoxData.mbAboveAverage ) + aReplaceFormula = maOoxData.mbEqualAverage ? CREATE_OUSTRING( "#B>=AVERAGE(#R)" ) : CREATE_OUSTRING( "#B>AVERAGE(#R)" ); + else + aReplaceFormula = maOoxData.mbEqualAverage ? CREATE_OUSTRING( "#B<=AVERAGE(#R)" ) : CREATE_OUSTRING( "#B<AVERAGE(#R)" ); + } + break; + } + + if( aReplaceFormula.getLength() > 0 ) + { + OUString aAddress, aRanges, aText; + sal_Int32 nStrPos = aReplaceFormula.getLength(); + while( (nStrPos = aReplaceFormula.lastIndexOf( '#', nStrPos )) >= 0 ) + { + switch( aReplaceFormula[ nStrPos + 1 ] ) + { + case 'B': // current base address + if( aAddress.getLength() == 0 ) + aAddress = FormulaProcessorBase::generateAddress2dString( mrCondFormat.getRanges().getBaseAddress(), false ); + aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aAddress ); + break; + case 'R': // range list of conditional formatting + if( aRanges.getLength() == 0 ) + aRanges = FormulaProcessorBase::generateRangeList2dString( mrCondFormat.getRanges(), true, ',', true ); + aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aRanges ); + break; + case 'T': // comparison text + if( aText.getLength() == 0 ) + { + // handle quote characters in comparison text + aText = maOoxData.maText; + sal_Int32 nQuotePos = aText.getLength(); + while( (nQuotePos = aText.lastIndexOf( '"', nQuotePos )) >= 0 ) + aText = aText.replaceAt( nQuotePos, 1, CREATE_OUSTRING( "\"\"" ) ); + aText = OUStringBuffer().append( sal_Unicode( '"' ) ).append( aText ).append( sal_Unicode( '"' ) ).makeStringAndClear(); + } + aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aText ); + break; + case 'L': // length of comparison text + aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, + OUString::valueOf( maOoxData.maText.getLength() ) ); + break; + case 'K': // top-10 rank + aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, + OUString::valueOf( maOoxData.mnRank ) ); + break; + case 'M': // top-10 top/bottom flag + aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, + OUString::valueOf( static_cast< sal_Int32 >( maOoxData.mbBottom ? 1 : 0 ) ) ); + break; + default: + OSL_ENSURE( false, "CondFormatRule::finalizeImport - unknown placeholder" ); + } + } + + // set the replacement formula + maOoxData.maFormulas.clear(); + appendFormula( aReplaceFormula ); + eOperator = ::com::sun::star::sheet::ConditionOperator_FORMULA; + } + + if( rxEntries.is() && (eOperator != ::com::sun::star::sheet::ConditionOperator_NONE) && !maOoxData.maFormulas.empty() ) + { + ::std::vector< PropertyValue > aProps; + // create condition properties + lclAppendProperty( aProps, CREATE_OUSTRING( "Operator" ), eOperator ); + lclAppendProperty( aProps, CREATE_OUSTRING( "Formula1" ), maOoxData.maFormulas[ 0 ].getTokens() ); + if( maOoxData.maFormulas.size() >= 2 ) + lclAppendProperty( aProps, CREATE_OUSTRING( "Formula2" ), maOoxData.maFormulas[ 1 ].getTokens() ); + + // style name for the formatting attributes + OUString aStyleName = getStyles().createDxfStyle( maOoxData.mnDxfId ); + if( aStyleName.getLength() > 0 ) + lclAppendProperty( aProps, CREATE_OUSTRING( "StyleName" ), aStyleName ); + + // append the new rule + try + { + rxEntries->addNew( ContainerHelper::vectorToSequence( aProps ) ); + } + catch( Exception& ) + { + } + } +} + +// ============================================================================ + +OoxCondFormatData::OoxCondFormatData() : + mbPivot( false ) +{ +} + +// ============================================================================ + +CondFormat::CondFormat( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ) +{ +} + +void CondFormat::importConditionalFormatting( const AttributeList& rAttribs ) +{ + getAddressConverter().convertToCellRangeList( maOoxData.maRanges, rAttribs.getString( XML_sqref ), getSheetIndex(), true ); + maOoxData.mbPivot = rAttribs.getBool( XML_pivot, false ); +} + +CondFormatRuleRef CondFormat::importCfRule( const AttributeList& rAttribs ) +{ + CondFormatRuleRef xRule = createRule(); + xRule->importCfRule( rAttribs ); + insertRule( xRule ); + return xRule; +} + +void CondFormat::importCondFormatting( RecordInputStream& rStrm ) +{ + BinRangeList aRanges; + rStrm.skip( 8 ); + rStrm >> aRanges; + getAddressConverter().convertToCellRangeList( maOoxData.maRanges, aRanges, getSheetIndex(), true ); +} + +void CondFormat::importCfRule( RecordInputStream& rStrm ) +{ + CondFormatRuleRef xRule = createRule(); + xRule->importCfRule( rStrm ); + insertRule( xRule ); +} + +void CondFormat::importCfHeader( BiffInputStream& rStrm ) +{ + // import the CFHEADER record + sal_uInt16 nRuleCount; + BinRangeList aRanges; + rStrm >> nRuleCount; + rStrm.skip( 10 ); + rStrm >> aRanges; + getAddressConverter().convertToCellRangeList( maOoxData.maRanges, aRanges, getSheetIndex(), true ); + + // import following list of CFRULE records + for( sal_uInt16 nRule = 0; (nRule < nRuleCount) && (rStrm.getNextRecId() == BIFF_ID_CFRULE) && rStrm.startNextRecord(); ++nRule ) + { + CondFormatRuleRef xRule = createRule(); + xRule->importCfRule( rStrm, nRule + 1 ); + insertRule( xRule ); + } +} + +void CondFormat::finalizeImport() +{ + Reference< XSheetCellRanges > xRanges = getCellRangeList( maOoxData.maRanges ); + if( xRanges.is() ) + { + PropertySet aPropSet( xRanges ); + Reference< XSheetConditionalEntries > xEntries; + aPropSet.getProperty( xEntries, CREATE_OUSTRING( "ConditionalFormat" ) ); + if( xEntries.is() ) + { + // maRules is sorted by rule priority + maRules.forEachMem( &CondFormatRule::finalizeImport, xEntries ); + aPropSet.setProperty( CREATE_OUSTRING( "ConditionalFormat" ), xEntries ); + } + } +} + +CondFormatRuleRef CondFormat::createRule() +{ + return CondFormatRuleRef( new CondFormatRule( *this ) ); +} + +void CondFormat::insertRule( CondFormatRuleRef xRule ) +{ + if( xRule.get() && (xRule->getPriority() > 0) ) + { + OSL_ENSURE( maRules.find( xRule->getPriority() ) == maRules.end(), "CondFormat::insertRule - multiple rules with equal priority" ); + maRules[ xRule->getPriority() ] = xRule; + } +} + +// ============================================================================ + +CondFormatBuffer::CondFormatBuffer( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ) +{ +} + +CondFormatRef CondFormatBuffer::importConditionalFormatting( const AttributeList& rAttribs ) +{ + CondFormatRef xCondFmt = createCondFormat(); + xCondFmt->importConditionalFormatting( rAttribs ); + return xCondFmt; +} + +CondFormatRef CondFormatBuffer::importCondFormatting( RecordInputStream& rStrm ) +{ + CondFormatRef xCondFmt = createCondFormat(); + xCondFmt->importCondFormatting( rStrm ); + return xCondFmt; +} + +void CondFormatBuffer::importCfHeader( BiffInputStream& rStrm ) +{ + createCondFormat()->importCfHeader( rStrm ); +} + +void CondFormatBuffer::finalizeImport() +{ + maCondFormats.forEachMem( &CondFormat::finalizeImport ); +} + +// private -------------------------------------------------------------------- + +CondFormatRef CondFormatBuffer::createCondFormat() +{ + CondFormatRef xCondFmt( new CondFormat( *this ) ); + maCondFormats.push_back( xCondFmt ); + return xCondFmt; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/condformatcontext.cxx b/oox/source/xls/condformatcontext.cxx new file mode 100644 index 000000000000..d387bc959cca --- /dev/null +++ b/oox/source/xls/condformatcontext.cxx @@ -0,0 +1,114 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: condformatcontext.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/condformatcontext.hxx" + +using ::rtl::OUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +OoxCondFormatContext::OoxCondFormatContext( const OoxWorksheetFragmentBase& rFragment ) : + OoxWorksheetContextBase( rFragment ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxCondFormatContext::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( conditionalFormatting ): + return (nElement == XLS_TOKEN( cfRule )); + case XLS_TOKEN( cfRule ): + return (nElement == XLS_TOKEN( formula )); + } + return false; +} + +void OoxCondFormatContext::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( conditionalFormatting ): + mxCondFmt = getCondFormats().importConditionalFormatting( rAttribs ); + break; + case XLS_TOKEN( cfRule ): + if( mxCondFmt.get() ) mxRule = mxCondFmt->importCfRule( rAttribs ); + break; + } +} + +void OoxCondFormatContext::onEndElement( const OUString& rChars ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( formula ): + if( mxCondFmt.get() && mxRule.get() ) mxRule->appendFormula( rChars ); + break; + } +} + +bool OoxCondFormatContext::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_CONDFORMATTING: + return (nRecId == OOBIN_ID_CFRULE); + } + return false; +} + +void OoxCondFormatContext::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_CONDFORMATTING: + mxCondFmt = getCondFormats().importCondFormatting( rStrm ); + break; + case OOBIN_ID_CFRULE: + if( mxCondFmt.get() ) mxCondFmt->importCfRule( rStrm ); + break; + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/connectionsfragment.cxx b/oox/source/xls/connectionsfragment.cxx new file mode 100644 index 000000000000..125e79cccc45 --- /dev/null +++ b/oox/source/xls/connectionsfragment.cxx @@ -0,0 +1,127 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: connectionsfragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/connectionsfragment.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/xls/webquerybuffer.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::xml::sax::SAXException; + +namespace oox { +namespace xls { + +OoxConnectionsFragment::OoxConnectionsFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ) +{ +} + +bool OoxConnectionsFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: return (nElement == XLS_TOKEN( connections )); + case XLS_TOKEN( connections ): return (nElement == XLS_TOKEN( connection )); + case XLS_TOKEN( connection ): return (nElement == XLS_TOKEN( webPr )) || + (nElement == XLS_TOKEN( textPr )) || + (nElement == XLS_TOKEN( dbPr )) || + (nElement == XLS_TOKEN( olapPr )) || + (nElement == XLS_TOKEN( parameters )); + case XLS_TOKEN( webPr ): return (nElement == XLS_TOKEN( tables )); + case XLS_TOKEN( tables ): return (nElement == XLS_TOKEN( m )) || + (nElement == XLS_TOKEN( s )) || + (nElement == XLS_TOKEN( x )); + } + return false; +} + +void OoxConnectionsFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch ( getCurrentContext() ) + { + case XLS_TOKEN( connection ): + importConnection( rAttribs ); + break; + case XLS_TOKEN( webPr ): + importWebPr( rAttribs ); + break; + case XLS_TOKEN( tables ): + importTables( rAttribs ); + break; + case XLS_TOKEN( s ): + importS( rAttribs ); + break; + case XLS_TOKEN( x ): + importX( rAttribs ); + break; + } +} + +void OoxConnectionsFragment::importConnection( const AttributeList& rAttribs ) +{ + if ( rAttribs.getInteger( XML_type, 0 ) == Connection::CONNECTION_WEBQUERY ) + { + getWebQueries().importConnection( rAttribs ); + } +} + +void OoxConnectionsFragment::importWebPr( const AttributeList& rAttribs ) +{ + getWebQueries().importWebPr( rAttribs ); +} + +void OoxConnectionsFragment::importTables( const AttributeList& /*rAttribs*/ ) +{ +// sal_Int32 nCount = rAttribs.getInteger( XML_count, 0 ); +} + +void OoxConnectionsFragment::importS( const AttributeList& /*rAttribs*/ ) +{ +// OUString aName = rAttribs.getString( XML_v ); +} + +void OoxConnectionsFragment::importX( const AttributeList& /*rAttribs*/ ) +{ +// sal_Int32 nSharedId = rAttribs.getInteger( XML_v, 0 ); +} + +} // namespace xls +} // namespace oox diff --git a/oox/source/xls/defnamesbuffer.cxx b/oox/source/xls/defnamesbuffer.cxx new file mode 100644 index 000000000000..6fc1623dea0e --- /dev/null +++ b/oox/source/xls/defnamesbuffer.cxx @@ -0,0 +1,674 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: defnamesbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/defnamesbuffer.hxx" +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/sheet/XNamedRanges.hpp> +#include <com/sun/star/sheet/XNamedRange.hpp> +#include <com/sun/star/sheet/XFormulaTokens.hpp> +#include <com/sun/star/sheet/XPrintAreas.hpp> +#include <com/sun/star/sheet/ReferenceFlags.hpp> +#include <com/sun/star/sheet/SingleReference.hpp> +#include <com/sun/star/sheet/ComplexReference.hpp> +#include <com/sun/star/sheet/NamedRangeFlag.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/externallinkbuffer.hxx" +#include "oox/xls/formulaparser.hxx" +#include "oox/xls/ooxtokens.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::sheet::SingleReference; +using ::com::sun::star::sheet::ComplexReference; +using ::com::sun::star::sheet::XNamedRanges; +using ::com::sun::star::sheet::XNamedRange; +using ::com::sun::star::sheet::XFormulaTokens; +using ::com::sun::star::sheet::XPrintAreas; +using namespace ::com::sun::star::sheet::ReferenceFlags; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt16 BIFF_DEFNAME_HIDDEN = 0x0001; +const sal_uInt16 BIFF_DEFNAME_FUNC = 0x0002; +const sal_uInt16 BIFF_DEFNAME_VBNAME = 0x0004; +const sal_uInt16 BIFF_DEFNAME_MACRO = 0x0008; +const sal_uInt16 BIFF_DEFNAME_CALCEXP = 0x0010; +const sal_uInt16 BIFF_DEFNAME_BUILTIN = 0x0020; +const sal_uInt16 BIFF_DEFNAME_FGROUPMASK = 0x0FC0; +const sal_uInt16 BIFF_DEFNAME_BIG = 0x1000; + +const sal_uInt8 BIFF2_DEFNAME_FUNC = 0x02; /// BIFF2 function/command flag. + +const sal_uInt16 BIFF_DEFNAME_GLOBAL = 0; /// 0 = Globally defined name. + +} // namespace + +// ============================================================================ + +OoxDefinedNameData::OoxDefinedNameData() : + mnSheet( -1 ), + mbMacro( false ), + mbFunction( false ), + mbVBName( false ), + mbHidden( false ) +{ +} + +// ============================================================================ + +namespace { + +const sal_uInt16 BIFF_REFFLAG_COL1REL = 0x0001; +const sal_uInt16 BIFF_REFFLAG_ROW1REL = 0x0002; +const sal_uInt16 BIFF_REFFLAG_COL2REL = 0x0004; +const sal_uInt16 BIFF_REFFLAG_ROW2REL = 0x0008; + +void lclConvertRefFlags( sal_Int32& ornFlags, sal_Int32& ornAbsPos, sal_Int32& ornRelPos, sal_Int32 nBasePos, sal_Int32 nApiRelFlag, bool bRel ) +{ + if( getFlag( ornFlags, nApiRelFlag ) && !bRel ) + { + // convert relative to absolute + setFlag( ornFlags, nApiRelFlag, false ); + ornAbsPos = nBasePos + ornRelPos; + } + else if( !getFlag( ornFlags, nApiRelFlag ) && bRel ) + { + // convert absolute to relative + setFlag( ornFlags, nApiRelFlag, true ); + ornRelPos = ornAbsPos - nBasePos; + } +} + +void lclConvertSingleRefFlags( SingleReference& orApiRef, const CellAddress& rBaseAddress, bool bColRel, bool bRowRel ) +{ + lclConvertRefFlags( + orApiRef.Flags, orApiRef.Column, orApiRef.RelativeColumn, + rBaseAddress.Column, COLUMN_RELATIVE, bColRel ); + lclConvertRefFlags( + orApiRef.Flags, orApiRef.Row, orApiRef.RelativeRow, + rBaseAddress.Row, ROW_RELATIVE, bRowRel ); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +DefinedNameBase::DefinedNameBase( const WorkbookHelper& rHelper, sal_Int32 nLocalSheet ) : + WorkbookHelper( rHelper ) +{ + maOoxData.mnSheet = nLocalSheet; +} + +Any DefinedNameBase::getReference( const CellAddress& rBaseAddress ) const +{ + if( maRefAny.hasValue() && (maOoxData.maName.getLength() >= 2) && (maOoxData.maName[ 0 ] == '\x01') ) + { + sal_Unicode cFlagsChar = maOoxData.maName.toAsciiUpperCase()[ 1 ]; + if( ('A' <= cFlagsChar) && (cFlagsChar <= 'P') ) + { + sal_uInt16 nFlags = static_cast< sal_uInt16 >( cFlagsChar - 'A' ); + if( maRefAny.has< SingleReference >() && (cFlagsChar <= 'D') ) + { + SingleReference aApiRef; + maRefAny >>= aApiRef; + lclConvertSingleRefFlags( aApiRef, rBaseAddress, getFlag( nFlags, BIFF_REFFLAG_COL1REL ), getFlag( nFlags, BIFF_REFFLAG_ROW1REL ) ); + return Any( aApiRef ); + } + if( maRefAny.has< ComplexReference >() ) + { + ComplexReference aApiRef; + maRefAny >>= aApiRef; + lclConvertSingleRefFlags( aApiRef.Reference1, rBaseAddress, getFlag( nFlags, BIFF_REFFLAG_COL1REL ), getFlag( nFlags, BIFF_REFFLAG_ROW1REL ) ); + lclConvertSingleRefFlags( aApiRef.Reference2, rBaseAddress, getFlag( nFlags, BIFF_REFFLAG_COL2REL ), getFlag( nFlags, BIFF_REFFLAG_ROW2REL ) ); + return Any( aApiRef ); + } + } + } + return Any(); +} + +void DefinedNameBase::importOoxFormula( FormulaContext& rContext ) +{ + if( maOoxData.maFormula.getLength() > 0 ) + { + rContext.setBaseAddress( CellAddress( static_cast< sal_Int16 >( maOoxData.mnSheet ), 0, 0 ) ); + getFormulaParser().importFormula( rContext, maOoxData.maFormula ); + } + else + getFormulaParser().convertErrorToFormula( rContext, BIFF_ERR_NAME ); +} + +void DefinedNameBase::importOobFormula( FormulaContext& rContext, RecordInputStream& rStrm ) +{ + rContext.setBaseAddress( CellAddress( static_cast< sal_Int16 >( maOoxData.mnSheet ), 0, 0 ) ); + getFormulaParser().importFormula( rContext, rStrm ); +} + +void DefinedNameBase::importBiffFormula( FormulaContext& rContext, BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ) +{ + rContext.setBaseAddress( CellAddress( static_cast< sal_Int16 >( maOoxData.mnSheet ), 0, 0 ) ); + if( !pnFmlaSize || (*pnFmlaSize > 0) ) + getFormulaParser().importFormula( rContext, rStrm, pnFmlaSize ); + else + getFormulaParser().convertErrorToFormula( rContext, BIFF_ERR_NAME ); +} + +void DefinedNameBase::setReference( const ApiTokenSequence& rTokens ) +{ + maRefAny = getFormulaParser().extractReference( rTokens ); +} + +// ============================================================================ + +namespace { + +const sal_Char* const spcLegacyPrefix = "Excel_BuiltIn_"; +const sal_Char* const spcOoxPrefix = "_xlnm."; + +const sal_Char* const sppcBaseNames[] = +{ + "Consolidate_Area", /* OOX */ + "Auto_Open", + "Auto_Close", + "Extract", /* OOX */ + "Database", /* OOX */ + "Criteria", /* OOX */ + "Print_Area", /* OOX */ + "Print_Titles", /* OOX */ + "Recorder", + "Data_Form", + "Auto_Activate", + "Auto_Deactivate", + "Sheet_Title", /* OOX */ + "_FilterDatabase" /* OOX */ +}; + +/** Localized names for _xlnm._FilterDatabase as used in BIFF5. */ +const sal_Char* const sppcFilterDbNames[] = +{ + "_FilterDatabase", // English + "_FilterDatenbank" // German +}; + +OUString lclGetBaseName( sal_Unicode cBuiltinId ) +{ + OSL_ENSURE( cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ), "lclGetBaseName - unknown builtin name" ); + OUStringBuffer aBuffer; + if( cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ) ) + aBuffer.appendAscii( sppcBaseNames[ cBuiltinId ] ); + else + aBuffer.append( static_cast< sal_Int32 >( cBuiltinId ) ); + return aBuffer.makeStringAndClear(); +} + +OUString lclGetFinalName( sal_Unicode cBuiltinId ) +{ + return OUStringBuffer().appendAscii( spcOoxPrefix ).append( lclGetBaseName( cBuiltinId ) ).makeStringAndClear(); +} + +sal_Unicode lclGetBuiltinIdFromOox( const OUString& rOoxName ) +{ + OUString aPrefix = OUString::createFromAscii( spcOoxPrefix ); + sal_Int32 nPrefixLen = aPrefix.getLength(); + if( rOoxName.matchIgnoreAsciiCase( aPrefix ) ) + { + for( sal_Unicode cBuiltinId = 0; cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ); ++cBuiltinId ) + { + OUString aBaseName = lclGetBaseName( cBuiltinId ); + sal_Int32 nBaseNameLen = aBaseName.getLength(); + if( (rOoxName.getLength() == nPrefixLen + nBaseNameLen) && rOoxName.matchIgnoreAsciiCase( aBaseName, nPrefixLen ) ) + return cBuiltinId; + } + } + return OOX_DEFNAME_UNKNOWN; +} + +sal_Unicode lclGetBuiltinIdFromOob( const OUString& rOobName ) +{ + for( sal_Unicode cBuiltinId = 0; cBuiltinId < STATIC_ARRAY_SIZE( sppcBaseNames ); ++cBuiltinId ) + if( rOobName.equalsIgnoreAsciiCaseAscii( sppcBaseNames[ cBuiltinId ] ) ) + return cBuiltinId; + return OOX_DEFNAME_UNKNOWN; +} + +bool lclIsFilterDatabaseName( const OUString& rName ) +{ + for( const sal_Char* const* ppcName = sppcFilterDbNames; ppcName < STATIC_ARRAY_END( sppcFilterDbNames ); ++ppcName ) + if( rName.equalsIgnoreAsciiCaseAscii( *ppcName ) ) + return true; + return false; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +DefinedName::DefinedName( const WorkbookHelper& rHelper, sal_Int32 nLocalSheet ) : + DefinedNameBase( rHelper, nLocalSheet ), + mnTokenIndex( -1 ), + mcBuiltinId( OOX_DEFNAME_UNKNOWN ), + mpStrm( 0 ), + mnRecHandle( -1 ), + mnRecPos( 0 ), + mnFmlaSize( 0 ) +{ +} + +void DefinedName::importDefinedName( const AttributeList& rAttribs ) +{ + maOoxData.maName = rAttribs.getString( XML_name ); + maOoxData.mnSheet = rAttribs.getInteger( XML_localSheetId, -1 ); + maOoxData.mbMacro = rAttribs.getBool( XML_xlm, false ); + maOoxData.mbFunction = rAttribs.getBool( XML_function, false ); + maOoxData.mbVBName = rAttribs.getBool( XML_vbProcedure, false ); + maOoxData.mbHidden = rAttribs.getBool( XML_hidden, false ); + mcBuiltinId = lclGetBuiltinIdFromOox( maOoxData.maName ); +} + +void DefinedName::setFormula( const OUString& rFormula ) +{ + maOoxData.maFormula = rFormula; +} + +void DefinedName::importDefinedName( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags; + rStrm.skip( 3 ); // 2 bytes unknown, 1 byte keyboard shortcut + rStrm >> maOoxData.mnSheet >> maOoxData.maName; + + // macro function/command, hidden flag, equal flags in BIFF and OOBIN + maOoxData.mbMacro = getFlag( nFlags, BIFF_DEFNAME_MACRO ); + maOoxData.mbFunction = getFlag( nFlags, BIFF_DEFNAME_FUNC ); + maOoxData.mbVBName = getFlag( nFlags, BIFF_DEFNAME_VBNAME ); + maOoxData.mbHidden = getFlag( nFlags, BIFF_DEFNAME_HIDDEN ); + + // get builtin name index from name + if( getFlag( nFlags, BIFF_DEFNAME_BUILTIN ) ) + mcBuiltinId = lclGetBuiltinIdFromOob( maOoxData.maName ); + // unhide built-in names (_xlnm._FilterDatabase is always hidden) + if( isBuiltinName() ) + maOoxData.mbHidden = false; + + // store token array data + sal_Int32 nRecPos = rStrm.getRecPos(); + sal_Int32 nFmlaSize = rStrm.readInt32(); + rStrm.skip( nFmlaSize ); + sal_Int32 nAddDataSize = rStrm.readInt32(); + if( rStrm.isValid() && (nFmlaSize > 0) && (nAddDataSize >= 0) && (rStrm.getRecLeft() >= nAddDataSize) ) + { + sal_Int32 nTotalSize = 8 + nFmlaSize + nAddDataSize; + mxFormula.reset( new RecordDataSequence( nTotalSize ) ); + rStrm.seek( nRecPos ); + rStrm.read( mxFormula->getArray(), nTotalSize ); + } +} + +void DefinedName::importDefinedName( BiffInputStream& rStrm ) +{ + BiffType eBiff = getBiff(); + sal_uInt16 nFlags = 0; + sal_Int16 nRefId = BIFF_DEFNAME_GLOBAL; + sal_Int16 nTabId = BIFF_DEFNAME_GLOBAL; + sal_uInt8 nNameLen = 0, nShortCut = 0; + + switch( eBiff ) + { + case BIFF2: + { + sal_uInt8 nFlagsBiff2; + rStrm >> nFlagsBiff2; + rStrm.skip( 1 ); + rStrm >> nShortCut >> nNameLen; + mnFmlaSize = rStrm.readuInt8(); + setFlag( nFlags, BIFF_DEFNAME_FUNC, getFlag( nFlagsBiff2, BIFF2_DEFNAME_FUNC ) ); + maOoxData.maName = rStrm.readCharArray( nNameLen, getTextEncoding() ); + } + break; + case BIFF3: + case BIFF4: + rStrm >> nFlags >> nShortCut >> nNameLen >> mnFmlaSize; + maOoxData.maName = rStrm.readCharArray( nNameLen, getTextEncoding() ); + break; + case BIFF5: + rStrm >> nFlags >> nShortCut >> nNameLen >> mnFmlaSize >> nRefId >> nTabId; + maOoxData.maName = rStrm.skip( 4 ).readCharArray( nNameLen, getTextEncoding() ); + break; + case BIFF8: + rStrm >> nFlags >> nShortCut >> nNameLen >> mnFmlaSize >> nRefId >> nTabId; + maOoxData.maName = rStrm.skip( 4 ).readUniString( nNameLen ); + break; + case BIFF_UNKNOWN: break; + } + + // macro function/command, hidden flag + maOoxData.mbMacro = getFlag( nFlags, BIFF_DEFNAME_MACRO ); + maOoxData.mbFunction = getFlag( nFlags, BIFF_DEFNAME_FUNC ); + maOoxData.mbVBName = getFlag( nFlags, BIFF_DEFNAME_VBNAME ); + maOoxData.mbHidden = getFlag( nFlags, BIFF_DEFNAME_HIDDEN ); + + // get builtin name index from name + if( getFlag( nFlags, BIFF_DEFNAME_BUILTIN ) ) + { + OSL_ENSURE( maOoxData.maName.getLength() == 1, "DefinedName::importDefinedName - wrong builtin name" ); + if( maOoxData.maName.getLength() > 0 ) + { + mcBuiltinId = maOoxData.maName[ 0 ]; + if( mcBuiltinId == '?' ) // the NUL character is imported as '?' + mcBuiltinId = OOX_DEFNAME_CONSOLIDATEAREA; + } + } + /* In BIFF5, _xlnm._FilterDatabase appears as hidden user name without + built-in flag, and even worse, localized. */ + else if( (eBiff == BIFF5) && lclIsFilterDatabaseName( maOoxData.maName ) ) + { + mcBuiltinId = OOX_DEFNAME_FILTERDATABASE; + } + + // unhide built-in names (_xlnm._FilterDatabase is always hidden) + if( isBuiltinName() ) + maOoxData.mbHidden = false; + + // get sheet index for sheet-local names in BIFF5-BIFF8 + switch( getBiff() ) + { + case BIFF2: + case BIFF3: + case BIFF4: + break; + case BIFF5: + // #i44019# nTabId may be invalid, resolve nRefId to sheet index + if( nRefId != BIFF_DEFNAME_GLOBAL ) + if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() ) + if( pExtLink->getLinkType() == LINKTYPE_INTERNAL ) + maOoxData.mnSheet = pExtLink->getSheetIndex(); + break; + case BIFF8: + // one-based sheet index + if( nTabId != BIFF_DEFNAME_GLOBAL ) + maOoxData.mnSheet = nTabId - 1; + break; + case BIFF_UNKNOWN: + break; + } + + // store record position to be able to import token array later + mpStrm = &rStrm; + mnRecHandle = rStrm.getRecHandle(); + mnRecPos = rStrm.getRecPos(); +} + +void DefinedName::createNameObject() +{ + // do not create hidden names and names for (macro) functions + if( maOoxData.mbHidden || maOoxData.mbFunction ) + return; + + // convert original name to final Calc name + if( maOoxData.mbVBName ) + maFinalName = maOoxData.maName; + else if( isBuiltinName() ) + maFinalName = lclGetFinalName( mcBuiltinId ); + else + maFinalName = maOoxData.maName; //! TODO convert to valid name + + // append sheet index for local names in multi-sheet documents + if( isWorkbookFile() && !isGlobalName() ) + maFinalName = OUStringBuffer( maFinalName ).append( sal_Unicode( '_' ) ).append( maOoxData.mnSheet + 1 ).makeStringAndClear(); + + // special flags for this name + sal_Int32 nNameFlags = 0; + using namespace ::com::sun::star::sheet::NamedRangeFlag; + if( !isGlobalName() ) switch( mcBuiltinId ) + { + case OOX_DEFNAME_CRITERIA: nNameFlags = FILTER_CRITERIA; break; + case OOX_DEFNAME_PRINTAREA: nNameFlags = PRINT_AREA; break; + case OOX_DEFNAME_PRINTTITLES: nNameFlags = COLUMN_HEADER | ROW_HEADER; break; + } + + // create the name and insert it into the document, maFinalName will be changed to the resulting name + mxNamedRange = getDefinedNames().createDefinedName( maFinalName, nNameFlags ); + // index of this defined name used in formula token arrays + mnTokenIndex = getDefinedNames().getTokenIndex( mxNamedRange ); +} + +void DefinedName::convertFormula() +{ + Reference< XFormulaTokens > xTokens( mxNamedRange, UNO_QUERY ); + if( xTokens.is() ) + { + // convert and set formula of the defined name + SimpleFormulaContext aContext( xTokens, true, true ); + switch( getFilterType() ) + { + case FILTER_OOX: implImportOoxFormula( aContext ); break; + case FILTER_BIFF: implImportBiffFormula( aContext ); break; + case FILTER_UNKNOWN: break; + } + + // set builtin names (print ranges, repeated titles, filter ranges) + if( !isGlobalName() ) switch( mcBuiltinId ) + { + case OOX_DEFNAME_PRINTAREA: + { + Reference< XPrintAreas > xPrintAreas( getSheet( maOoxData.mnSheet ), UNO_QUERY ); + ApiCellRangeList aPrintRanges; + getFormulaParser().extractCellRangeList( aPrintRanges, xTokens->getTokens(), maOoxData.mnSheet ); + if( xPrintAreas.is() && !aPrintRanges.empty() ) + xPrintAreas->setPrintAreas( ContainerHelper::vectorToSequence( aPrintRanges ) ); + } + break; + case OOX_DEFNAME_PRINTTITLES: + { + Reference< XPrintAreas > xPrintAreas( getSheet( maOoxData.mnSheet ), UNO_QUERY ); + ApiCellRangeList aTitleRanges; + getFormulaParser().extractCellRangeList( aTitleRanges, xTokens->getTokens(), maOoxData.mnSheet ); + if( xPrintAreas.is() && !aTitleRanges.empty() ) + { + bool bHasRowTitles = false; + bool bHasColTitles = false; + const CellAddress& rMaxPos = getAddressConverter().getMaxAddress(); + for( ApiCellRangeList::const_iterator aIt = aTitleRanges.begin(), aEnd = aTitleRanges.end(); (aIt != aEnd) && (!bHasRowTitles || !bHasColTitles); ++aIt ) + { + bool bFullRow = (aIt->StartColumn == 0) && (aIt->EndColumn >= rMaxPos.Column); + bool bFullCol = (aIt->StartRow == 0) && (aIt->EndRow >= rMaxPos.Row); + if( !bHasRowTitles && bFullRow && !bFullCol ) + { + xPrintAreas->setTitleRows( *aIt ); + xPrintAreas->setPrintTitleRows( sal_True ); + bHasRowTitles = true; + } + else if( !bHasColTitles && bFullCol && !bFullRow ) + { + xPrintAreas->setTitleColumns( *aIt ); + xPrintAreas->setPrintTitleColumns( sal_True ); + bHasColTitles = true; + } + } + } + } + break; + } + } + else if( maOoxData.mbHidden && (maOoxData.maName.getLength() > 0) && (maOoxData.maName[ 0 ] == '\x01') ) + { + // import BIFF2-BIFF4 external references + TokensFormulaContext aContext( true, true ); + implImportBiffFormula( aContext ); + setReference( aContext.getTokens() ); + } +} + +void DefinedName::implImportOoxFormula( FormulaContext& rContext ) +{ + if( mxFormula.get() ) + { + RecordInputStream aStrm( *mxFormula ); + importOobFormula( rContext, aStrm ); + } + else + importOoxFormula( rContext ); +} + +void DefinedName::implImportBiffFormula( FormulaContext& rContext ) +{ + OSL_ENSURE( mpStrm, "DefinedName::importBiffFormula - missing BIFF stream" ); + sal_Int64 nCurrRecHandle = mpStrm->getRecHandle(); + if( mpStrm->startRecordByHandle( mnRecHandle ) ) + mpStrm->seek( mnRecPos ); + importBiffFormula( rContext, *mpStrm, &mnFmlaSize ); + mpStrm->startRecordByHandle( nCurrRecHandle ); +} + +// ============================================================================ + +DefinedNamesBuffer::DefinedNamesBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maTokenIndexProp( CREATE_OUSTRING( "TokenIndex" ) ), + mnLocalSheet( -1 ) +{ +} + +Reference< XNamedRange > DefinedNamesBuffer::createDefinedName( OUString& orName, sal_Int32 nNameFlags ) const +{ + // find an unused name + Reference< XNamedRanges > xNamedRanges = getNamedRanges(); + Reference< XNameAccess > xNameAccess( xNamedRanges, UNO_QUERY ); + if( xNameAccess.is() ) + orName = ContainerHelper::getUnusedName( xNameAccess, orName, '_' ); + + // create the name and insert it into the Calc document + Reference< XNamedRange > xNamedRange; + if( xNamedRanges.is() && (orName.getLength() > 0) ) try + { + xNamedRanges->addNewByName( orName, OUString(), CellAddress( 0, 0, 0 ), nNameFlags ); + xNamedRange.set( xNamedRanges->getByName( orName ), UNO_QUERY ); + } + catch( Exception& ) + { + OSL_ENSURE( false, "DefinedNamesBuffer::createDefinedName - cannot create defined name" ); + } + return xNamedRange; +} + +sal_Int32 DefinedNamesBuffer::getTokenIndex( const Reference< XNamedRange >& rxNamedRange ) const +{ + PropertySet aPropSet( rxNamedRange ); + sal_Int32 nIndex = -1; + return aPropSet.getProperty( nIndex, maTokenIndexProp ) ? nIndex : -1; +} + +void DefinedNamesBuffer::setLocalSheetIndex( sal_Int32 nLocalSheet ) +{ + mnLocalSheet = nLocalSheet; +} + +DefinedNameRef DefinedNamesBuffer::importDefinedName( const AttributeList& rAttribs ) +{ + DefinedNameRef xDefName = createDefinedName(); + xDefName->importDefinedName( rAttribs ); + return xDefName; +} + +void DefinedNamesBuffer::importDefinedName( RecordInputStream& rStrm ) +{ + createDefinedName()->importDefinedName( rStrm ); +} + +void DefinedNamesBuffer::importDefinedName( BiffInputStream& rStrm ) +{ + createDefinedName()->importDefinedName( rStrm ); +} + +void DefinedNamesBuffer::finalizeImport() +{ + /* First insert all names without formula definition into the document. */ + maDefNames.forEachMem( &DefinedName::createNameObject ); + /* Now convert all name formulas, so that the formula parser can find all + names in case of circular dependencies. */ + maDefNames.forEachMem( &DefinedName::convertFormula ); +} + +DefinedNameRef DefinedNamesBuffer::getByIndex( sal_Int32 nIndex ) const +{ + return maDefNames.get( nIndex ); +} + +DefinedNameRef DefinedNamesBuffer::getByOoxName( const OUString& rOoxName, sal_Int32 nSheet ) const +{ + DefinedNameRef xGlobalName; // a found global name + DefinedNameRef xLocalName; // a found local name + for( DefNameVec::const_iterator aIt = maDefNames.begin(), aEnd = maDefNames.end(); (aIt != aEnd) && !xLocalName; ++aIt ) + { + DefinedNameRef xCurrName = *aIt; + if( xCurrName->getOoxName() == rOoxName ) + { + if( xCurrName->getSheetIndex() == nSheet ) + xLocalName = xCurrName; + else if( xCurrName->isGlobalName() ) + xGlobalName = xCurrName; + } + } + return xLocalName.get() ? xLocalName : xGlobalName; +} + +DefinedNameRef DefinedNamesBuffer::createDefinedName() +{ + DefinedNameRef xDefName( new DefinedName( *this, mnLocalSheet ) ); + maDefNames.push_back( xDefName ); + return xDefName; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/excelfilter.cxx b/oox/source/xls/excelfilter.cxx new file mode 100644 index 000000000000..feaf77188ee6 --- /dev/null +++ b/oox/source/xls/excelfilter.cxx @@ -0,0 +1,284 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: excelfilter.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/excelfilter.hxx" +#include "oox/helper/binaryinputstream.hxx" +#include "oox/xls/biffdetector.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/themebuffer.hxx" +#include "oox/xls/workbookfragment.hxx" +#include "oox/dump/biffdumper.hxx" +#include "oox/dump/xlsbdumper.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::xml::sax::XFastDocumentHandler; +using ::oox::core::BinaryFilterBase; +using ::oox::core::FragmentHandlerRef; +using ::oox::core::RecordInfo; +using ::oox::core::RecordInfoProvider; +using ::oox::core::RecordInfoProviderRef; +using ::oox::core::Relation; +using ::oox::core::Relations; +using ::oox::core::XmlFilterBase; +using ::oox::vml::DrawingPtr; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +/** List of OOBIN record identifiers that start a new context level. */ +static const struct RecordInfo spRecInfos[] = +{ + { OOBIN_ID_BOOKVIEWS, OOBIN_ID_BOOKVIEWS + 1 }, + { OOBIN_ID_BORDERS, OOBIN_ID_BORDERS + 1 }, + { OOBIN_ID_CELLSTYLES, OOBIN_ID_CELLSTYLES + 1 }, + { OOBIN_ID_CELLSTYLEXFS, OOBIN_ID_CELLSTYLEXFS + 1 }, + { OOBIN_ID_CELLXFS, OOBIN_ID_CELLXFS + 1 }, + { OOBIN_ID_CFRULE, OOBIN_ID_CFRULE + 1 }, + { OOBIN_ID_COLBREAKS, OOBIN_ID_COLBREAKS + 1 }, + { OOBIN_ID_COLORS, OOBIN_ID_COLORS + 1 }, + { OOBIN_ID_COLORSCALE, OOBIN_ID_COLORSCALE + 1 }, + { OOBIN_ID_COLS, OOBIN_ID_COLS + 1 }, + { OOBIN_ID_CONDFORMATTING, OOBIN_ID_CONDFORMATTING + 1 }, + { OOBIN_ID_DATABAR, OOBIN_ID_DATABAR + 1 }, + { OOBIN_ID_DATAVALIDATIONS, OOBIN_ID_DATAVALIDATIONS + 1 }, + { OOBIN_ID_DDEITEMVALUES, OOBIN_ID_DDEITEMVALUES + 1 }, + { OOBIN_ID_DXFS, OOBIN_ID_DXFS + 1 }, + { OOBIN_ID_EXTERNALBOOK, -1 }, + { OOBIN_ID_EXTERNALREFS, OOBIN_ID_EXTERNALREFS + 1 }, + { OOBIN_ID_EXTROW, -1 }, + { OOBIN_ID_EXTSHEETDATA, OOBIN_ID_EXTSHEETDATA + 1 }, + { OOBIN_ID_FILLS, OOBIN_ID_FILLS + 1 }, + { OOBIN_ID_FONTS, OOBIN_ID_FONTS + 1 }, + { OOBIN_ID_HEADERFOOTER, OOBIN_ID_HEADERFOOTER + 1 }, + { OOBIN_ID_ICONSET, OOBIN_ID_ICONSET + 1 }, + { OOBIN_ID_INDEXEDCOLORS, OOBIN_ID_INDEXEDCOLORS + 1 }, + { OOBIN_ID_MERGECELLS, OOBIN_ID_MERGECELLS + 1 }, + { OOBIN_ID_MRUCOLORS, OOBIN_ID_MRUCOLORS + 1 }, + { OOBIN_ID_NUMFMTS, OOBIN_ID_NUMFMTS + 1 }, + { OOBIN_ID_ROW, -1 }, + { OOBIN_ID_ROWBREAKS, OOBIN_ID_ROWBREAKS + 1 }, + { OOBIN_ID_SHEETDATA, OOBIN_ID_SHEETDATA + 1 }, + { OOBIN_ID_SHEETDATASET, OOBIN_ID_SHEETDATASET + 1 }, + { OOBIN_ID_SHEETS, OOBIN_ID_SHEETS + 1 }, + { OOBIN_ID_SHEETVIEW, OOBIN_ID_SHEETVIEW + 1 }, + { OOBIN_ID_SHEETVIEWS, OOBIN_ID_SHEETVIEWS + 1 }, + { OOBIN_ID_SST, OOBIN_ID_SST + 1 }, + { OOBIN_ID_STYLESHEET, OOBIN_ID_STYLESHEET + 1 }, + { OOBIN_ID_TABLE, OOBIN_ID_TABLE + 1 }, + { OOBIN_ID_TABLEPARTS, OOBIN_ID_TABLEPARTS + 2 }, // end element increased by 2! + { OOBIN_ID_TABLESTYLES, OOBIN_ID_TABLESTYLES + 1 }, + { OOBIN_ID_VOLTYPE, OOBIN_ID_VOLTYPE + 1 }, + { OOBIN_ID_VOLTYPEMAIN, OOBIN_ID_VOLTYPEMAIN + 1 }, + { OOBIN_ID_VOLTYPES, OOBIN_ID_VOLTYPES + 1 }, + { OOBIN_ID_WORKBOOK, OOBIN_ID_WORKBOOK + 1 }, + { OOBIN_ID_WORKSHEET, OOBIN_ID_WORKSHEET + 1 }, + { -1, -1 } +}; + +} // namespace + +// ============================================================================ + +OUString SAL_CALL ExcelFilter_getImplementationName() throw() +{ + return CREATE_OUSTRING( "com.sun.star.comp.oox.ExcelFilter" ); +} + +Sequence< OUString > SAL_CALL ExcelFilter_getSupportedServiceNames() throw() +{ + OUString aServiceName = CREATE_OUSTRING( "com.sun.star.comp.oox.ExcelFilter" ); + Sequence< OUString > aSeq( &aServiceName, 1 ); + return aSeq; +} + +Reference< XInterface > SAL_CALL ExcelFilter_createInstance( + const Reference< XMultiServiceFactory >& rxFactory ) throw( Exception ) +{ + return static_cast< ::cppu::OWeakObject* >( new ExcelFilter( rxFactory ) ); +} + +// ---------------------------------------------------------------------------- + +ExcelFilter::ExcelFilter( const Reference< XMultiServiceFactory >& rxFactory ) : + XmlFilterBase( rxFactory ), + mpHelper( 0 ) +{ +} + +ExcelFilter::~ExcelFilter() +{ +} + +bool ExcelFilter::importDocument() throw() +{ +#if OOX_INCLUDE_DUMPER + { + ::oox::dump::xlsb::Dumper aDumper( *this ); + aDumper.dump(); + if( !aDumper.isImportEnabled() ) + return aDumper.isValid(); + } +#endif + + bool bRet = false; + OUString aWorkbookPath = getFragmentPathFromType( CREATE_RELATIONS_TYPE( "officeDocument" ) ); + if( aWorkbookPath.getLength() > 0 ) + { + WorkbookHelperRoot aHelper( *this ); + if( aHelper.isValid() ) + { + mpHelper = &aHelper; // needed for callbacks + bRet = importFragment( new OoxWorkbookFragment( aHelper, aWorkbookPath ) ); + mpHelper = 0; + } + } + return bRet; +} + +bool ExcelFilter::exportDocument() throw() +{ + return false; +} + +sal_Int32 ExcelFilter::getSchemeClr( sal_Int32 nColorSchemeToken ) const +{ + OSL_ENSURE( mpHelper, "ExcelFilter::getSchemeClr - no workbook helper" ); + return mpHelper ? mpHelper->getTheme().getColorByToken( nColorSchemeToken ) : -1; +} + +const DrawingPtr ExcelFilter::getDrawings() +{ + return DrawingPtr(); +} + +RecordInfoProviderRef ExcelFilter::getRecordInfoProvider() +{ + if( !mxRecInfoProv ) + mxRecInfoProv.reset( new RecordInfoProvider( spRecInfos ) ); + return mxRecInfoProv; +} + +OUString ExcelFilter::implGetImplementationName() const +{ + return ExcelFilter_getImplementationName(); +} + +// ============================================================================ + +OUString SAL_CALL ExcelBiffFilter_getImplementationName() throw() +{ + return CREATE_OUSTRING( "com.sun.star.comp.oox.ExcelBiffFilter" ); +} + +Sequence< OUString > SAL_CALL ExcelBiffFilter_getSupportedServiceNames() throw() +{ + OUString aServiceName = CREATE_OUSTRING( "com.sun.star.comp.oox.ExcelBiffFilter" ); + Sequence< OUString > aSeq( &aServiceName, 1 ); + return aSeq; +} + +Reference< XInterface > SAL_CALL ExcelBiffFilter_createInstance( + const Reference< XMultiServiceFactory >& rxFactory ) throw( Exception ) +{ + return static_cast< ::cppu::OWeakObject* >( new ExcelBiffFilter( rxFactory ) ); +} + +// ---------------------------------------------------------------------------- + +ExcelBiffFilter::ExcelBiffFilter( const Reference< XMultiServiceFactory >& rxFactory ) : + BinaryFilterBase( rxFactory ) +{ +} + +ExcelBiffFilter::~ExcelBiffFilter() +{ +} + +bool ExcelBiffFilter::importDocument() throw() +{ +#if OOX_INCLUDE_DUMPER + { + ::oox::dump::biff::Dumper aDumper( *this ); + aDumper.dump(); + if( !aDumper.isImportEnabled() ) + return aDumper.isValid(); + } +#endif + + bool bRet = false; + + // detect BIFF version and workbook stream name + OUString aWorkbookName; + BiffType eBiff = BiffDetector::detectStorageBiffVersion( aWorkbookName, getStorage() ); + BinaryInputStream aInStrm( getStorage()->openInputStream( aWorkbookName ), aWorkbookName.getLength() > 0 ); + OSL_ENSURE( (eBiff != BIFF_UNKNOWN) && aInStrm.is(), "ExcelBiffFilter::ExcelBiffFilter - invalid file format" ); + + if( (eBiff != BIFF_UNKNOWN) && aInStrm.is() ) + { + WorkbookHelperRoot aHelper( *this, eBiff ); + if( aHelper.isValid() ) + { + BiffWorkbookFragment aFragment( aHelper ); + BiffInputStream aBiffStream( aInStrm ); + bRet = aFragment.importFragment( aBiffStream ); + } + } + return bRet; +} + +bool ExcelBiffFilter::exportDocument() throw() +{ + return false; +} + +OUString ExcelBiffFilter::implGetImplementationName() const +{ + return ExcelBiffFilter_getImplementationName(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/externallinkbuffer.cxx b/oox/source/xls/externallinkbuffer.cxx new file mode 100644 index 000000000000..72dab2171955 --- /dev/null +++ b/oox/source/xls/externallinkbuffer.cxx @@ -0,0 +1,857 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: externallinkbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/externallinkbuffer.hxx" +#include <rtl/strbuf.hxx> +#include <com/sun/star/sheet/XDDELinks.hpp> +#include <com/sun/star/sheet/XDDELink.hpp> +#include <com/sun/star/sheet/XDDELinkResults.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/formulaparser.hxx" +#include "oox/xls/ooxfragmenthandler.hxx" +#include "oox/xls/worksheetbuffer.hxx" + +using ::rtl::OString; +using ::rtl::OStringBuffer; +using ::rtl::OStringToOUString; +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::sheet::XDDELinks; +using ::com::sun::star::sheet::XDDELinkResults; +using ::oox::core::Relations; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt16 OOBIN_EXTERNALBOOK_BOOK = 0; +const sal_uInt16 OOBIN_EXTERNALBOOK_DDE = 1; +const sal_uInt16 OOBIN_EXTERNALBOOK_OLE = 2; + +const sal_uInt16 OOBIN_EXTNAME_AUTOMATIC = 0x0002; +const sal_uInt16 OOBIN_EXTNAME_PREFERPIC = 0x0004; +const sal_uInt16 OOBIN_EXTNAME_STDDOCNAME = 0x0008; +const sal_uInt16 OOBIN_EXTNAME_OLEOBJECT = 0x0010; +const sal_uInt16 OOBIN_EXTNAME_ICONIFIED = 0x0020; + +const sal_uInt16 BIFF_EXTNAME_BUILTIN = 0x0001; +const sal_uInt16 BIFF_EXTNAME_AUTOMATIC = 0x0002; +const sal_uInt16 BIFF_EXTNAME_PREFERPIC = 0x0004; +const sal_uInt16 BIFF_EXTNAME_STDDOCNAME = 0x0008; +const sal_uInt16 BIFF_EXTNAME_OLEOBJECT = 0x0010; +const sal_uInt16 BIFF_EXTNAME_ICONIFIED = 0x8000; + +} // namespace + +// ============================================================================ + +OoxExternalNameData::OoxExternalNameData() : + mbBuiltIn( false ), + mbNotify( false ), + mbPreferPic( false ), + mbStdDocName( false ), + mbOleObj( false ), + mbIconified( false ) +{ +} + +// ============================================================================ + +ExternalName::ExternalName( const ExternalLink& rParentLink, sal_Int32 nLocalSheet ) : + DefinedNameBase( rParentLink, nLocalSheet ), + mrParentLink( rParentLink ), + mnStorageId( 0 ), + mbDdeLinkCreated( false ) +{ +} + +void ExternalName::importDefinedName( const AttributeList& rAttribs ) +{ + maOoxData.maName = rAttribs.getString( XML_name ); + OSL_ENSURE( maOoxData.maName.getLength() > 0, "ExternalName::importDefinedName - empty name" ); + // zero-based index into sheet list of externalBook + maOoxData.mnSheet = mrParentLink.getSheetIndex( rAttribs.getInteger( XML_sheetId, -1 ) ); +} + +void ExternalName::importDdeItem( const AttributeList& rAttribs ) +{ + maOoxData.maName = rAttribs.getString( XML_name ); + OSL_ENSURE( maOoxData.maName.getLength() > 0, "ExternalName::importDdeItem - empty name" ); + maOoxExtNameData.mbOleObj = false; + maOoxExtNameData.mbStdDocName = rAttribs.getBool( XML_ole, false ); + maOoxExtNameData.mbNotify = rAttribs.getBool( XML_advise, false ); + maOoxExtNameData.mbPreferPic = rAttribs.getBool( XML_preferPic, false ); +} + +void ExternalName::importValues( const AttributeList& rAttribs ) +{ + setResultSize( rAttribs.getInteger( XML_cols, 1 ), rAttribs.getInteger( XML_rows, 1 ) ); +} + +void ExternalName::importOleItem( const AttributeList& rAttribs ) +{ + maOoxData.maName = rAttribs.getString( XML_name ); + OSL_ENSURE( maOoxData.maName.getLength() > 0, "ExternalName::importOleItem - empty name" ); + maOoxExtNameData.mbOleObj = true; + maOoxExtNameData.mbNotify = rAttribs.getBool( XML_advise, false ); + maOoxExtNameData.mbPreferPic = rAttribs.getBool( XML_preferPic, false ); + maOoxExtNameData.mbIconified = rAttribs.getBool( XML_icon, false ); +} + +void ExternalName::importExternalName( RecordInputStream& rStrm ) +{ + rStrm >> maOoxData.maName; + OSL_ENSURE( maOoxData.maName.getLength() > 0, "ExternalName::importExternalName - empty name" ); +} + +void ExternalName::importExternalNameFlags( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + sal_Int32 nSheetId; + rStrm >> nFlags >> nSheetId; + // one-based index into sheet list of EXTSHEETNAMES + maOoxData.mnSheet = mrParentLink.getSheetIndex( nSheetId - 1 ); + // no flag for built-in names, as in OOX... + maOoxExtNameData.mbNotify = getFlag( nFlags, OOBIN_EXTNAME_AUTOMATIC ); + maOoxExtNameData.mbPreferPic = getFlag( nFlags, OOBIN_EXTNAME_PREFERPIC ); + maOoxExtNameData.mbStdDocName = getFlag( nFlags, OOBIN_EXTNAME_STDDOCNAME ); + maOoxExtNameData.mbOleObj = getFlag( nFlags, OOBIN_EXTNAME_OLEOBJECT ); + maOoxExtNameData.mbIconified = getFlag( nFlags, OOBIN_EXTNAME_ICONIFIED ); + OSL_ENSURE( (mrParentLink.getLinkType() == LINKTYPE_OLE) == maOoxExtNameData.mbOleObj, + "ExternalName::importExternalNameFlags - wrong flags in external name" ); +} + +void ExternalName::importDdeItemValues( RecordInputStream& rStrm ) +{ + sal_Int32 nRows, nCols; + rStrm >> nRows >> nCols; + setResultSize( nCols, nRows ); +} + +void ExternalName::importDdeItemBool( RecordInputStream& rStrm ) +{ + appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 ); +} + +void ExternalName::importDdeItemDouble( RecordInputStream& rStrm ) +{ + appendResultValue( rStrm.readDouble() ); +} + +void ExternalName::importDdeItemError( RecordInputStream& rStrm ) +{ + appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) ); +} + +void ExternalName::importDdeItemString( RecordInputStream& rStrm ) +{ + appendResultValue( rStrm.readString() ); +} + +void ExternalName::importExternalName( BiffInputStream& rStrm ) +{ + sal_uInt16 nFlags = 0; + if( getBiff() >= BIFF3 ) + { + rStrm >> nFlags; + maOoxExtNameData.mbBuiltIn = getFlag( nFlags, BIFF_EXTNAME_BUILTIN ); + maOoxExtNameData.mbNotify = getFlag( nFlags, BIFF_EXTNAME_AUTOMATIC ); + maOoxExtNameData.mbPreferPic = getFlag( nFlags, BIFF_EXTNAME_PREFERPIC ); + + // BIFF5-BIFF8: sheet index for sheet-local names, OLE settings + if( getBiff() >= BIFF5 ) + { + maOoxExtNameData.mbStdDocName = getFlag( nFlags, BIFF_EXTNAME_STDDOCNAME ); + maOoxExtNameData.mbOleObj = getFlag( nFlags, BIFF_EXTNAME_OLEOBJECT ); + maOoxExtNameData.mbIconified = getFlag( nFlags, BIFF_EXTNAME_ICONIFIED ); + + if( maOoxExtNameData.mbOleObj ) + { + rStrm >> mnStorageId; + } + else + { + // get sheet index for sheet-local names + sal_Int16 nRefId = rStrm.skip( 2 ).readuInt16(); + switch( getBiff() ) + { + case BIFF2: + case BIFF3: + case BIFF4: + break; + case BIFF5: + // resolve nRefId to sheet index, zero is global name + if( nRefId > 0 ) + if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() ) + if( pExtLink->getLinkType() == LINKTYPE_EXTERNAL ) + maOoxData.mnSheet = pExtLink->getSheetIndex(); + break; + case BIFF8: + // one-based index into sheet list of EXTERNALBOOK record, zero is global name + if( nRefId > 0 ) + maOoxData.mnSheet = mrParentLink.getSheetIndex( nRefId - 1 ); + break; + case BIFF_UNKNOWN: + break; + } + } + } + } + + maOoxData.maName = (getBiff() == BIFF8) ? + rStrm.readUniString( rStrm.readuInt8() ) : + rStrm.readByteString( false, getTextEncoding() ); + OSL_ENSURE( maOoxData.maName.getLength() > 0, "ExternalName::importExternalName - empty name" ); + + switch( mrParentLink.getLinkType() ) + { + case LINKTYPE_EXTERNAL: + // external cell references that are stored in hidden external names (seen in BIFF3-BIFF4) + if( (maOoxData.maName.getLength() > 0) && (maOoxData.maName[ 0 ] == '\x01') && (rStrm.getRecLeft() > 2) ) + { + TokensFormulaContext aContext( true, true ); + importBiffFormula( aContext, rStrm ); + setReference( aContext.getTokens() ); + } + break; + + case LINKTYPE_DDE: + case LINKTYPE_OLE: + case LINKTYPE_MAYBE_DDE_OLE: + // DDE/OLE link results + if( rStrm.getRecLeft() > 3 ) + { + bool bBiff8 = getBiff() == BIFF8; + sal_Int32 nCols = rStrm.readuInt8(); + sal_Int32 nRows = rStrm.readuInt16(); + if( bBiff8 ) { ++nCols; ++nRows; } else if( nCols == 0 ) nCols = 256; + setResultSize( nCols, nRows ); + + bool bLoop = true; + while( bLoop && rStrm.isValid() && (maCurrIt != maResults.end()) ) + { + switch( rStrm.readuInt8() ) + { + case BIFF_DATATYPE_EMPTY: + appendResultValue( OUString() ); + rStrm.skip( 8 ); + break; + case BIFF_DATATYPE_DOUBLE: + appendResultValue( rStrm.readDouble() ); + break; + case BIFF_DATATYPE_STRING: + appendResultValue( bBiff8 ? rStrm.readUniString() : rStrm.readByteString( false, getTextEncoding() ) ); + break; + case BIFF_DATATYPE_BOOL: + appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 ); + rStrm.skip( 7 ); + break; + case BIFF_DATATYPE_ERROR: + appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) ); + rStrm.skip( 7 ); + break; + default: + bLoop = false; + } + } + OSL_ENSURE( bLoop && rStrm.isValid() && (maCurrIt == maResults.end()), + "ExternalName::importExternalName - stream error in result set" ); + } + break; + + default:; + } +} + +bool ExternalName::getDdeLinkData( OUString& orDdeServer, OUString& orDdeTopic, OUString& orDdeItem ) +{ + if( (mrParentLink.getLinkType() == LINKTYPE_DDE) && (maOoxData.maName.getLength() > 0) ) + { + // try to create a DDE link and to set the imported link results + if( !mbDdeLinkCreated ) try + { + Reference< XDDELinks > xDdeLinks( getDdeLinks(), UNO_QUERY_THROW ); + mxDdeLink = xDdeLinks->addDDELink( mrParentLink.getClassName(), mrParentLink.getTargetUrl(), maOoxData.maName, ::com::sun::star::sheet::DDELinkMode_DEFAULT ); + if( !maResults.empty() ) + { + Reference< XDDELinkResults > xResults( mxDdeLink, UNO_QUERY_THROW ); + xResults->setResults( ContainerHelper::matrixToSequenceSequence( maResults ) ); + } + mbDdeLinkCreated = true; + } + catch( Exception& ) + { + OSL_ENSURE( false, "ExternalName::getDdeLinkData - cannot create DDE link" ); + } + // get link data from created DDE link + if( mxDdeLink.is() ) + { + orDdeServer = mxDdeLink->getApplication(); + orDdeTopic = mxDdeLink->getTopic(); + orDdeItem = mxDdeLink->getItem(); + return true; + } + } + return false; +} + +void ExternalName::setResultSize( sal_Int32 nColumns, sal_Int32 nRows ) +{ + OSL_ENSURE( (mrParentLink.getLinkType() == LINKTYPE_DDE) || (mrParentLink.getLinkType() == LINKTYPE_OLE) || + (mrParentLink.getLinkType() == LINKTYPE_MAYBE_DDE_OLE), "ExternalName::setResultSize - wrong link type" ); + OSL_ENSURE( (nRows > 0) && (nColumns > 0), "ExternalName::setResultSize - invalid matrix size" ); + const CellAddress& rMaxPos = getAddressConverter().getMaxApiAddress(); + if( (0 < nRows) && (nRows <= rMaxPos.Row + 1) && (0 < nColumns) && (nColumns <= rMaxPos.Column + 1) ) + maResults.resize( static_cast< size_t >( nColumns ), static_cast< size_t >( nRows ), Any( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) ) ); + else + maResults.clear(); + maCurrIt = maResults.begin(); +} + +// ============================================================================ + +ExternalLink::ExternalLink( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + meLinkType( LINKTYPE_UNKNOWN ) +{ +} + +void ExternalLink::importExternalReference( const AttributeList& rAttribs ) +{ + maRelId = rAttribs.getString( R_TOKEN( id ) ); +} + +void ExternalLink::importExternalBook( const Relations& rRelations, const AttributeList& rAttribs ) +{ + OUString aTargetUrl = rRelations.getTargetFromRelId( rAttribs.getString( R_TOKEN( id ) ) ); + setExternalTargetUrl( aTargetUrl ); +} + +void ExternalLink::importSheetName( const AttributeList& rAttribs ) +{ + OUString aSheetName = rAttribs.getString( XML_val ); + OSL_ENSURE( aSheetName.getLength() > 0, "ExternalLink::importSheetName - empty sheet name" ); + if( meLinkType == LINKTYPE_EXTERNAL ) + maSheetIndexes.push_back( getWorksheets().insertExternalSheet( maTargetUrl, aSheetName ) ); +} + +void ExternalLink::importDefinedName( const AttributeList& rAttribs ) +{ + createExternalName()->importDefinedName( rAttribs ); +} + +void ExternalLink::importDdeLink( const AttributeList& rAttribs ) +{ + OUString aDdeService = rAttribs.getString( XML_ddeService ); + OUString aDdeTopic = rAttribs.getString( XML_ddeTopic ); + setDdeOleTargetUrl( aDdeService, aDdeTopic, LINKTYPE_DDE ); +} + +ExternalNameRef ExternalLink::importDdeItem( const AttributeList& rAttribs ) +{ + ExternalNameRef xExtName = createExternalName(); + xExtName->importDdeItem( rAttribs ); + return xExtName; +} + +void ExternalLink::importOleLink( const Relations& rRelations, const AttributeList& rAttribs ) +{ + OUString aProgId = rAttribs.getString( XML_progId ); + OUString aTargetUrl = rRelations.getTargetFromRelId( rAttribs.getString( R_TOKEN( id ) ) ); + setDdeOleTargetUrl( aProgId, aTargetUrl, LINKTYPE_OLE ); +} + +ExternalNameRef ExternalLink::importOleItem( const AttributeList& rAttribs ) +{ + ExternalNameRef xExtName = createExternalName(); + xExtName->importOleItem( rAttribs ); + return xExtName; +} + +void ExternalLink::importExternalRef( RecordInputStream& rStrm ) +{ + rStrm >> maRelId; +} + +void ExternalLink::importExternalSelf( RecordInputStream& ) +{ + meLinkType = LINKTYPE_SELF; +} + +void ExternalLink::importExternalBook( const Relations& rRelations, RecordInputStream& rStrm ) +{ + switch( rStrm.readuInt16() ) + { + case OOBIN_EXTERNALBOOK_BOOK: + { + OUString aTargetUrl = rRelations.getTargetFromRelId( rStrm.readString() ); + setExternalTargetUrl( aTargetUrl ); + } + break; + case OOBIN_EXTERNALBOOK_DDE: + { + OUString aDdeService, aDdeTopic; + rStrm >> aDdeService >> aDdeTopic; + setDdeOleTargetUrl( aDdeService, aDdeTopic, LINKTYPE_DDE ); + } + break; + case OOBIN_EXTERNALBOOK_OLE: + { + OUString aTargetUrl = rRelations.getTargetFromRelId( rStrm.readString() ); + OUString aProgId = rStrm.readString(); + setDdeOleTargetUrl( aProgId, aTargetUrl, LINKTYPE_OLE ); + } + break; + default: + OSL_ENSURE( false, "ExternalLink::importExternalBook - unknown link type" ); + } +} + +void ExternalLink::importExtSheetNames( RecordInputStream& rStrm ) +{ + // load external sheet names and create the linked sheets in the Calc document + OSL_ENSURE( meLinkType == LINKTYPE_EXTERNAL, "ExternalLink::importExtSheetNames - invalid link type" ); + if( meLinkType == LINKTYPE_EXTERNAL ) + { + WorksheetBuffer& rWorksheets = getWorksheets(); + for( sal_Int32 nSheet = 0, nCount = rStrm.readInt32(); rStrm.isValid() && (nSheet < nCount); ++nSheet ) + { + OUString aSheetName = rStrm.readString(); + OSL_ENSURE( aSheetName.getLength() > 0, "ExternalLink::importExtSheetNames - empty sheet name" ); + maSheetIndexes.push_back( rWorksheets.insertExternalSheet( maTargetUrl, aSheetName ) ); + } + } +} + +ExternalNameRef ExternalLink::importExternalName( RecordInputStream& rStrm ) +{ + ExternalNameRef xExtName = createExternalName(); + xExtName->importExternalName( rStrm ); + return xExtName; +} + +void ExternalLink::importExternSheet( BiffInputStream& rStrm ) +{ + OStringBuffer aTargetBuffer( rStrm.readByteString( false ) ); + // references to own sheets have wrong string length field (off by 1) + if( (aTargetBuffer.getLength() > 0) && (aTargetBuffer[ 0 ] == 3) ) + aTargetBuffer.append( static_cast< sal_Char >( rStrm.readuInt8() ) ); + // parse the encoded URL + OUString aBiffTarget = OStringToOUString( aTargetBuffer.makeStringAndClear(), getTextEncoding() ); + OUString aSheetName = parseBiffTargetUrl( aBiffTarget ); + switch( meLinkType ) + { + case LINKTYPE_INTERNAL: + maSheetIndexes.push_back( getWorksheets().getFinalSheetIndex( aSheetName ) ); + break; + case LINKTYPE_EXTERNAL: + maSheetIndexes.push_back( getWorksheets().insertExternalSheet( maTargetUrl, aSheetName ) ); + break; + default:; + } +} + +void ExternalLink::importExternalBook( BiffInputStream& rStrm ) +{ + OUString aTarget; + sal_uInt16 nSheetCount; + rStrm >> nSheetCount; + if( rStrm.getRecLeft() == 2 ) + { + if( rStrm.readuInt8() == 1 ) + { + sal_Char cChar = static_cast< sal_Char >( rStrm.readuInt8() ); + if( cChar != 0 ) + aTarget = OStringToOUString( OString( cChar ), getTextEncoding() ); + } + } + else if( rStrm.getRecLeft() >= 3 ) + { + aTarget = rStrm.readUniString(); + } + + // parse the encoded URL + OUString aDummySheetName = parseBiffTargetUrl( aTarget ); + OSL_ENSURE( aDummySheetName.getLength() == 0, "ExternalLink::importExternalBook - sheet name in encoded URL" ); + (void)aDummySheetName; // prevent compiler warning + + // load external sheet names and create the linked sheets in the Calc document + if( meLinkType == LINKTYPE_EXTERNAL ) + { + WorksheetBuffer& rWorksheets = getWorksheets(); + for( sal_uInt16 nSheet = 0; rStrm.isValid() && (nSheet < nSheetCount); ++nSheet ) + { + OUString aSheetName = rStrm.readUniString(); + OSL_ENSURE( aSheetName.getLength() > 0, "ExternalLink::importExternalBook - empty sheet name" ); + maSheetIndexes.push_back( rWorksheets.insertExternalSheet( maTargetUrl, aSheetName ) ); + } + } +} + +void ExternalLink::importExternalName( BiffInputStream& rStrm ) +{ + ExternalNameRef xExtName = createExternalName(); + xExtName->importExternalName( rStrm ); + switch( meLinkType ) + { + case LINKTYPE_DDE: + OSL_ENSURE( !xExtName->isOleObject(), "ExternalLink::importExternalName - OLE object in DDE link" ); + break; + case LINKTYPE_OLE: + OSL_ENSURE( xExtName->isOleObject(), "ExternalLink::importExternalName - anything but OLE object in OLE link" ); + break; + case LINKTYPE_MAYBE_DDE_OLE: + meLinkType = xExtName->isOleObject() ? LINKTYPE_OLE : LINKTYPE_DDE; + break; + default: + OSL_ENSURE( !xExtName->isOleObject(), "ExternalLink::importExternalName - OLE object in external name" ); + } +} + +sal_Int32 ExternalLink::getSheetIndex( sal_Int32 nTabId ) const +{ + OSL_ENSURE( (nTabId == 0) || (getFilterType() == FILTER_OOX) || (getBiff() == BIFF8), + "ExternalLink::getSheetIndex - invalid sheet index" ); + return ((0 <= nTabId) && (static_cast< size_t >( nTabId ) < maSheetIndexes.size())) ? + maSheetIndexes[ static_cast< size_t >( nTabId ) ] : -1; +} + +void ExternalLink::getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const +{ + orSheetRange.setDeleted(); + switch( meLinkType ) + { + case LINKTYPE_SELF: + case LINKTYPE_INTERNAL: + orSheetRange.set( nTabId1, nTabId2 ); + break; + case LINKTYPE_EXTERNAL: switch( getFilterType() ) + { + case FILTER_OOX: + // OOBIN: passed indexes point into sheet list of EXTSHEETLIST + orSheetRange.set( getSheetIndex( nTabId1 ), getSheetIndex( nTabId2 ) ); + break; + case FILTER_BIFF: + switch( getBiff() ) + { + case BIFF2: + case BIFF3: + case BIFF4: + orSheetRange.set( getSheetIndex( nTabId1 ), getSheetIndex( nTabId2 ) ); + break; + case BIFF5: + // BIFF5: first sheet from this external link, last sheet is passed in nTabId2 + if( const ExternalLink* pExtLink2 = getExternalLinks().getExternalLink( nTabId2 ).get() ) + if( (pExtLink2->getLinkType() == LINKTYPE_EXTERNAL) && (maTargetUrl == pExtLink2->getTargetUrl()) ) + orSheetRange.set( getSheetIndex(), pExtLink2->getSheetIndex() ); + break; + case BIFF8: + // BIFF8: passed indexes point into sheet list of EXTERNALBOOK + orSheetRange.set( getSheetIndex( nTabId1 ), getSheetIndex( nTabId2 ) ); + break; + case BIFF_UNKNOWN: break; + } + break; + case FILTER_UNKNOWN: + break; + } + break; + + default:; + } +} + +ExternalNameRef ExternalLink::getNameByIndex( sal_Int32 nIndex ) const +{ + return maExtNames.get( nIndex ); +} + +// private -------------------------------------------------------------------- + +void ExternalLink::setExternalTargetUrl( const OUString& rTargetUrl ) +{ + maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl ); + meLinkType = (maTargetUrl.getLength() > 0) ? LINKTYPE_EXTERNAL : LINKTYPE_UNKNOWN; + OSL_ENSURE( meLinkType == LINKTYPE_EXTERNAL, "ExternalLink::setExternalTargetUrl - empty target URL" ); +} + +void ExternalLink::setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType ) +{ + maClassName = rClassName; + maTargetUrl = rTargetUrl; + meLinkType = ((maClassName.getLength() > 0) && (maTargetUrl.getLength() > 0)) ? eLinkType : LINKTYPE_UNKNOWN; + OSL_ENSURE( meLinkType == eLinkType, "ExternalLink::setDdeOleTargetUrl - missing classname or target" ); +} + +OUString ExternalLink::parseBiffTargetUrl( const OUString& rBiffTargetUrl ) +{ + OUString aClassName, aTargetUrl, aSheetName; + meLinkType = LINKTYPE_UNKNOWN; + if( getAddressConverter().parseBiffTargetUrl( aClassName, aTargetUrl, aSheetName, rBiffTargetUrl ) ) + { + if( aClassName.getLength() > 0 ) + { + setDdeOleTargetUrl( aClassName, aTargetUrl, LINKTYPE_MAYBE_DDE_OLE ); + } + else if( aTargetUrl.getLength() == 0 ) + { + meLinkType = (aSheetName.getLength() > 0) ? LINKTYPE_INTERNAL : LINKTYPE_SELF; + } + else if( (aTargetUrl.getLength() == 1) && (aTargetUrl[ 0 ] == ':') ) + { + if( getBiff() >= BIFF4 ) + meLinkType = LINKTYPE_ANALYSIS; + } + else if( (aTargetUrl.getLength() == 1) && (aTargetUrl[ 0 ] == ' ') ) + { + meLinkType = LINKTYPE_UNKNOWN; + } + else + { + setExternalTargetUrl( aTargetUrl ); + } + } + return aSheetName; +} + +ExternalNameRef ExternalLink::createExternalName() +{ + ExternalNameRef xExtName( new ExternalName( *this, getSheetIndex() ) ); + maExtNames.push_back( xExtName ); + return xExtName; +} + +// ============================================================================ + +OoxRefSheets::OoxRefSheets() : + mnExtRefId( -1 ), + mnTabId1( -1 ), + mnTabId2( -1 ) +{ +} + +void OoxRefSheets::readOobData( RecordInputStream& rStrm ) +{ + rStrm >> mnExtRefId >> mnTabId1 >> mnTabId2; +} + +void OoxRefSheets::readBiff8Data( BiffInputStream& rStrm ) +{ + mnExtRefId = rStrm.readuInt16(); + mnTabId1 = rStrm.readInt16(); + mnTabId2 = rStrm.readInt16(); +} + +// ---------------------------------------------------------------------------- + +ExternalLinkBuffer::ExternalLinkBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mbUseRefSheets( false ) +{ +} + +ExternalLinkRef ExternalLinkBuffer::importExternalReference( const AttributeList& rAttribs ) +{ + ExternalLinkRef xExtLink = createExternalLink(); + xExtLink->importExternalReference( rAttribs ); + return xExtLink; +} + +ExternalLinkRef ExternalLinkBuffer::importExternalRef( RecordInputStream& rStrm ) +{ + mbUseRefSheets = true; + ExternalLinkRef xExtLink = createExternalLink(); + xExtLink->importExternalRef( rStrm ); + return xExtLink; +} + +void ExternalLinkBuffer::importExternalSelf( RecordInputStream& rStrm ) +{ + mbUseRefSheets = true; + createExternalLink()->importExternalSelf( rStrm ); +} + +void ExternalLinkBuffer::importExternalSheets( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::importExternalSheets - missing EXTERNALREFS records" ); + mbUseRefSheets = true; + OSL_ENSURE( maRefSheets.empty(), "ExternalLinkBuffer::importExternalSheets - multiple EXTERNALSHEETS records" ); + maRefSheets.clear(); + sal_Int32 nRefCount; + rStrm >> nRefCount; + size_t nMaxCount = getLimitedValue< size_t, sal_Int32 >( nRefCount, 0, rStrm.getRecLeft() / 12 ); + maRefSheets.reserve( nMaxCount ); + for( size_t nRefId = 0; rStrm.isValid() && (nRefId < nMaxCount); ++nRefId ) + { + OoxRefSheets aRefSheets; + aRefSheets.readOobData( rStrm ); + maRefSheets.push_back( aRefSheets ); + } +} + +ExternalLinkRef ExternalLinkBuffer::importExternSheet( BiffInputStream& rStrm ) +{ + OSL_ENSURE( getBiff() <= BIFF5, "ExternalLinkBuffer::importExternSheet - wrong BIFF version" ); + ExternalLinkRef xExtLink = createExternalLink(); + xExtLink->importExternSheet( rStrm ); + return xExtLink; +} + +ExternalLinkRef ExternalLinkBuffer::importExternalBook( BiffInputStream& rStrm ) +{ + ExternalLinkRef xExtLink = createExternalLink(); + xExtLink->importExternalBook( rStrm ); + return xExtLink; +} + +void ExternalLinkBuffer::importExternalName( BiffInputStream& rStrm ) +{ + if( !maExtLinks.empty() ) + maExtLinks.back()->importExternalName( rStrm ); +} + +void ExternalLinkBuffer::importExternSheet8( BiffInputStream& rStrm ) +{ + OSL_ENSURE( getBiff() == BIFF8, "ExternalLinkBuffer::importExternSheet - wrong BIFF version" ); + OSL_ENSURE( maRefSheets.empty(), "ExternalLinkBuffer::importExternSheet - multiple EXTERNSHEET records" ); + maRefSheets.clear(); + sal_uInt16 nRefCount; + rStrm >> nRefCount; + maRefSheets.reserve( nRefCount ); + for( sal_uInt16 nRefId = 0; rStrm.isValid() && (nRefId < nRefCount); ++nRefId ) + { + OoxRefSheets aRefSheets; + aRefSheets.readBiff8Data( rStrm ); + maRefSheets.push_back( aRefSheets ); + } +} + +ExternalLinkRef ExternalLinkBuffer::getExternalLink( sal_Int32 nRefId ) const +{ + ExternalLinkRef xExtLink; + switch( getFilterType() ) + { + case FILTER_OOX: + // OOXML: one-based index + if( !mbUseRefSheets ) + xExtLink = maExtLinks.get( nRefId - 1 ); + // OOBIN: zero-based index into ref-sheets list + else if( const OoxRefSheets* pRefSheets = getRefSheets( nRefId ) ) + xExtLink = maExtLinks.get( pRefSheets->mnExtRefId ); + break; + case FILTER_BIFF: + switch( getBiff() ) + { + case BIFF2: + case BIFF3: + case BIFF4: + // one-based index to EXTERNSHEET records + xExtLink = maExtLinks.get( nRefId - 1 ); + break; + case BIFF5: + if( nRefId < 0 ) + { + // internal links in formula tokens have negative index + xExtLink = maExtLinks.get( -nRefId - 1 ); + if( xExtLink.get() && !xExtLink->isInternalLink() ) + xExtLink.reset(); + } + else + { + // one-based index to EXTERNSHEET records + xExtLink = maExtLinks.get( nRefId - 1 ); + } + break; + case BIFF8: + // zero-based index into REF list in EXTERNSHEET record + if( const OoxRefSheets* pRefSheets = getRefSheets( nRefId ) ) + xExtLink = maExtLinks.get( pRefSheets->mnExtRefId ); + break; + case BIFF_UNKNOWN: break; + } + break; + case FILTER_UNKNOWN: break; + } + return xExtLink; +} + +LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId, sal_Int16 nTabId1, sal_Int16 nTabId2 ) const +{ + OSL_ENSURE( getBiff() <= BIFF5, "ExternalLinkBuffer::getSheetRange - wrong BIFF version" ); + LinkSheetRange aSheetRange; + if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() ) + pExtLink->getSheetRange( aSheetRange, nTabId1, nTabId2 ); + return aSheetRange; +} + +LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId ) const +{ + OSL_ENSURE( ((getFilterType() == FILTER_OOX) && mbUseRefSheets) || (getBiff() == BIFF8), "ExternalLinkBuffer::getSheetRange - wrong BIFF version" ); + LinkSheetRange aSheetRange; + if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() ) + if( const OoxRefSheets* pRefSheets = getRefSheets( nRefId ) ) + pExtLink->getSheetRange( aSheetRange, pRefSheets->mnTabId1, pRefSheets->mnTabId2 ); + return aSheetRange; +} + +// private -------------------------------------------------------------------- + +ExternalLinkRef ExternalLinkBuffer::createExternalLink() +{ + ExternalLinkRef xExtLink( new ExternalLink( *this ) ); + maExtLinks.push_back( xExtLink ); + return xExtLink; +} + +const OoxRefSheets* ExternalLinkBuffer::getRefSheets( sal_Int32 nRefId ) const +{ + return ((0 <= nRefId) && (static_cast< size_t >( nRefId ) < maRefSheets.size())) ? + &maRefSheets[ static_cast< size_t >( nRefId ) ] : 0; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/externallinkfragment.cxx b/oox/source/xls/externallinkfragment.cxx new file mode 100644 index 000000000000..b291a6ad7147 --- /dev/null +++ b/oox/source/xls/externallinkfragment.cxx @@ -0,0 +1,392 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: externallinkfragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/externallinkfragment.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/core/recordparser.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/defnamesbuffer.hxx" +#include "oox/xls/sheetdatacontext.hxx" +#include "oox/xls/unitconverter.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::xml::sax::XFastContextHandler; +using ::oox::core::RecordContextRef; +using ::oox::core::Relation; + +namespace oox { +namespace xls { + +// ============================================================================ + +OoxExternalLinkFragment::OoxExternalLinkFragment( const WorkbookHelper& rHelper, + const OUString& rFragmentPath, ExternalLink& rExtLink ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ), + mrExtLink( rExtLink ), + mnResultType( XML_TOKEN_INVALID ) +{ +} + +bool OoxExternalLinkFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nElement == XLS_TOKEN( externalLink )); + case XLS_TOKEN( externalLink ): + return (nElement == XLS_TOKEN( externalBook )) || + (nElement == XLS_TOKEN( ddeLink )) || + (nElement == XLS_TOKEN( oleLink )); + case XLS_TOKEN( externalBook ): + return (nElement == XLS_TOKEN( sheetNames )) || + (nElement == XLS_TOKEN( definedNames )) || + (nElement == XLS_TOKEN( sheetDataSet )); + case XLS_TOKEN( sheetNames ): + return (nElement == XLS_TOKEN( sheetName )); + case XLS_TOKEN( definedNames ): + return (nElement == XLS_TOKEN( definedName )); + case XLS_TOKEN( sheetDataSet ): + return (nElement == XLS_TOKEN( sheetData )); + case XLS_TOKEN( ddeLink ): + return (nElement == XLS_TOKEN( ddeItems )); + case XLS_TOKEN( ddeItems ): + return (nElement == XLS_TOKEN( ddeItem )); + case XLS_TOKEN( ddeItem ): + return (nElement == XLS_TOKEN( values )); + case XLS_TOKEN( values ): + return (nElement == XLS_TOKEN( value )); + case XLS_TOKEN( value ): + return (nElement == XLS_TOKEN( val )); + case XLS_TOKEN( oleLink ): + return (nElement == XLS_TOKEN( oleItems )); + case XLS_TOKEN( oleItems ): + return (nElement == XLS_TOKEN( oleItem )); + } + return false; +} + +Reference< XFastContextHandler > OoxExternalLinkFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + switch( nElement ) + { + case XLS_TOKEN( sheetData ): + if( mrExtLink.getLinkType() == LINKTYPE_EXTERNAL ) + { + sal_Int32 nSheet = mrExtLink.getSheetIndex( rAttribs.getInteger( XML_sheetId, -1 ) ); + Reference< XFastContextHandler > xHandler; + ::rtl::Reference< OoxExternalSheetDataContext > xContext( new OoxExternalSheetDataContext( *this, SHEETTYPE_WORKSHEET, nSheet ) ); + if( xContext->isValidSheet() ) + xHandler.set( xContext.get() ); + return xHandler; + } + break; + } + return this; +} + +void OoxExternalLinkFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( externalBook ): mrExtLink.importExternalBook( getRelations(), rAttribs ); break; + case XLS_TOKEN( sheetName ): mrExtLink.importSheetName( rAttribs ); break; + case XLS_TOKEN( definedName ): mrExtLink.importDefinedName( rAttribs ); break; + case XLS_TOKEN( ddeLink ): mrExtLink.importDdeLink( rAttribs ); break; + case XLS_TOKEN( ddeItem ): mxExtName = mrExtLink.importDdeItem( rAttribs ); break; + case XLS_TOKEN( values ): if( mxExtName.get() ) mxExtName->importValues( rAttribs ); break; + case XLS_TOKEN( value ): mnResultType = rAttribs.getToken( XML_t, XML_n ); break; + case XLS_TOKEN( oleLink ): mrExtLink.importOleLink( getRelations(), rAttribs ); break; + case XLS_TOKEN( oleItem ): mxExtName = mrExtLink.importOleItem( rAttribs ); break; + } +} + +void OoxExternalLinkFragment::onEndElement( const OUString& rChars ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( val ): + maResultValue = rChars; + break; + case XLS_TOKEN( value ): + if( mxExtName.get() ) switch( mnResultType ) + { + case XML_b: + mxExtName->appendResultValue( maResultValue.toDouble() ); + break; + case XML_e: + mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( maResultValue ) ) ); + break; + case XML_n: + mxExtName->appendResultValue( maResultValue.toDouble() ); + break; + case XML_str: + mxExtName->appendResultValue( maResultValue ); + break; + default: + mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) ); + } + break; + } +} + +bool OoxExternalLinkFragment::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + /* Weird things are going on in this fragment... + + Without external names, several EXTSHEETDATA/EXTSHEETDATA_END contexts + contain the external cells. They are not preceded by a SHEETDATASET + context record, but a SHEETDATASET_END record occurs at the end of the + stream. In this case we have to start a SHEETDATASET context on-the-fly + to keep the context stack valid. + */ + if( (getCurrentContext() == OOBIN_ID_EXTERNALBOOK) && (nRecId == OOBIN_ID_EXTSHEETDATA) ) + getRecordParser().pushContext( OOBIN_ID_SHEETDATASET, this ); + + /* With external names, SHEETDATASET contexts are opened after each + external name, but not closed before a new external name starts. Here + we have to close the SHEETDATASET context before. + */ + else if( (getCurrentContext() == OOBIN_ID_SHEETDATASET) && (nRecId != OOBIN_ID_EXTSHEETDATA) ) + getRecordParser().popContext(); + + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nRecId == OOBIN_ID_EXTERNALBOOK); + case OOBIN_ID_EXTERNALBOOK: + return (nRecId == OOBIN_ID_EXTSHEETNAMES) || + (nRecId == OOBIN_ID_EXTERNALNAME) || + (nRecId == OOBIN_ID_EXTERNALNAMEFLAGS) || + (nRecId == OOBIN_ID_SHEETDATASET) || + (nRecId == OOBIN_ID_DDEITEMVALUES); + case OOBIN_ID_SHEETDATASET: + return (nRecId == OOBIN_ID_EXTSHEETDATA); + case OOBIN_ID_DDEITEMVALUES: + return (nRecId == OOBIN_ID_DDEITEM_BOOL) || + (nRecId == OOBIN_ID_DDEITEM_DOUBLE) || + (nRecId == OOBIN_ID_DDEITEM_ERROR) || + (nRecId == OOBIN_ID_DDEITEM_STRING); + } + return false; +} + +RecordContextRef OoxExternalLinkFragment::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& rStrm ) +{ + switch( nRecId ) + { + case OOBIN_ID_EXTSHEETDATA: + if( mrExtLink.getLinkType() == LINKTYPE_EXTERNAL ) + { + sal_Int32 nSheet = mrExtLink.getSheetIndex( rStrm.readInt32() ); + RecordContextRef xRecContext; + ::rtl::Reference< OoxExternalSheetDataContext > xContext( new OoxExternalSheetDataContext( *this, SHEETTYPE_WORKSHEET, nSheet ) ); + if( xContext->isValidSheet() ) + xRecContext.set( xContext.get() ); + return xRecContext; + } + break; + } + return this; +} + +void OoxExternalLinkFragment::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_EXTERNALBOOK: mrExtLink.importExternalBook( getRelations(), rStrm ); break; + case OOBIN_ID_EXTSHEETNAMES: mrExtLink.importExtSheetNames( rStrm ); break; + case OOBIN_ID_EXTERNALNAME: mxExtName = mrExtLink.importExternalName( rStrm ); break; + case OOBIN_ID_EXTERNALNAMEFLAGS: if( mxExtName.get() ) mxExtName->importExternalNameFlags( rStrm ); break; + case OOBIN_ID_DDEITEMVALUES: if( mxExtName.get() ) mxExtName->importDdeItemValues( rStrm ); break; + case OOBIN_ID_DDEITEM_BOOL: if( mxExtName.get() ) mxExtName->importDdeItemBool( rStrm ); break; + case OOBIN_ID_DDEITEM_DOUBLE: if( mxExtName.get() ) mxExtName->importDdeItemDouble( rStrm ); break; + case OOBIN_ID_DDEITEM_ERROR: if( mxExtName.get() ) mxExtName->importDdeItemError( rStrm ); break; + case OOBIN_ID_DDEITEM_STRING: if( mxExtName.get() ) mxExtName->importDdeItemString( rStrm ); break; + } +} + +// ============================================================================ + +BiffExternalLinkFragment::BiffExternalLinkFragment( const WorkbookHelper& rHelper, bool bImportDefNames ) : + BiffWorkbookFragmentBase( rHelper ), + mbImportDefNames( bImportDefNames ) +{ +} + +BiffExternalLinkFragment::~BiffExternalLinkFragment() +{ +} + +bool BiffExternalLinkFragment::importFragment( BiffInputStream& rStrm ) +{ + // process all record in this sheet fragment + while( rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) ) + { + if( isBofRecord( rStrm.getRecId() ) ) + skipFragment( rStrm ); // skip unknown embedded fragments + else + importRecord( rStrm ); + } + return rStrm.isValid() && (rStrm.getRecId() == BIFF_ID_EOF); +} + +void BiffExternalLinkFragment::importRecord( BiffInputStream& rStrm ) +{ + sal_uInt16 nRecId = rStrm.getRecId(); + switch( getBiff() ) + { + case BIFF2: switch( nRecId ) + { + case BIFF2_ID_EXTERNALNAME: importExternalName( rStrm ); break; + case BIFF_ID_EXTERNSHEET: importExternSheet( rStrm ); break; + case BIFF2_ID_DEFINEDNAME: importDefinedName( rStrm ); break; + } + break; + case BIFF3: switch( nRecId ) + { + case BIFF_ID_CRN: importCrn( rStrm ); break; + case BIFF3_ID_EXTERNALNAME: importExternalName( rStrm ); break; + case BIFF_ID_EXTERNSHEET: importExternSheet( rStrm ); break; + case BIFF3_ID_DEFINEDNAME: importDefinedName( rStrm ); break; + case BIFF_ID_XCT: importXct( rStrm ); break; + } + break; + case BIFF4: switch( nRecId ) + { + case BIFF_ID_CRN: importCrn( rStrm ); break; + case BIFF3_ID_EXTERNALNAME: importExternalName( rStrm ); break; + case BIFF_ID_EXTERNSHEET: importExternSheet( rStrm ); break; + case BIFF3_ID_DEFINEDNAME: importDefinedName( rStrm ); break; + case BIFF_ID_XCT: importXct( rStrm ); break; + } + break; + case BIFF5: switch( nRecId ) + { + case BIFF_ID_CRN: importCrn( rStrm ); break; + case BIFF5_ID_EXTERNALNAME: importExternalName( rStrm ); break; + case BIFF_ID_EXTERNSHEET: importExternSheet( rStrm ); break; + case BIFF5_ID_DEFINEDNAME: importDefinedName( rStrm ); break; + case BIFF_ID_XCT: importXct( rStrm ); break; + } + break; + case BIFF8: switch( nRecId ) + { + case BIFF_ID_CRN: importCrn( rStrm ); break; + case BIFF_ID_EXTERNALBOOK: importExternalBook( rStrm ); break; + case BIFF5_ID_EXTERNALNAME: importExternalName( rStrm ); break; + case BIFF_ID_EXTERNSHEET: importExternSheet( rStrm ); break; + case BIFF5_ID_DEFINEDNAME: importDefinedName( rStrm ); break; + case BIFF_ID_XCT: importXct( rStrm ); break; + } + break; + case BIFF_UNKNOWN: break; + } +} + +void BiffExternalLinkFragment::finalizeImport() +{ + getDefinedNames().finalizeImport(); +} + +// private -------------------------------------------------------------------- + +void BiffExternalLinkFragment::importExternSheet( BiffInputStream& rStrm ) +{ + mxContext.reset(); + if( getBiff() == BIFF8 ) + getExternalLinks().importExternSheet8( rStrm ); + else + mxExtLink = getExternalLinks().importExternSheet( rStrm ); +} + +void BiffExternalLinkFragment::importExternalBook( BiffInputStream& rStrm ) +{ + mxContext.reset(); + mxExtLink = getExternalLinks().importExternalBook( rStrm ); +} + +void BiffExternalLinkFragment::importExternalName( BiffInputStream& rStrm ) +{ + if( mxExtLink.get() ) + mxExtLink->importExternalName( rStrm ); +} + +void BiffExternalLinkFragment::importXct( BiffInputStream& rStrm ) +{ + mxContext.reset(); + if( mxExtLink.get() && (mxExtLink->getLinkType() == LINKTYPE_EXTERNAL) ) + { + sal_Int32 nSheet = -1; + switch( getBiff() ) + { + case BIFF2: + break; + case BIFF3: + case BIFF4: + case BIFF5: + nSheet = mxExtLink->getSheetIndex(); + break; + case BIFF8: + rStrm.skip( 2 ); + nSheet = mxExtLink->getSheetIndex( rStrm.readInt16() ); + break; + case BIFF_UNKNOWN: break; + } + + // create a sheet data context to import the CRN records and set the cached cell values + mxContext.reset( new BiffExternalSheetDataContext( *this, SHEETTYPE_WORKSHEET, nSheet ) ); + if( !mxContext->isValidSheet() ) + mxContext.reset(); + } +} + +void BiffExternalLinkFragment::importCrn( BiffInputStream& rStrm ) +{ + if( mxContext.get() ) + mxContext->importCrn( rStrm ); +} + +void BiffExternalLinkFragment::importDefinedName( BiffInputStream& rStrm ) +{ + if( mbImportDefNames ) + getDefinedNames().importDefinedName( rStrm ); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/formulabase.cxx b/oox/source/xls/formulabase.cxx new file mode 100644 index 000000000000..4c5cb1bdfabc --- /dev/null +++ b/oox/source/xls/formulabase.cxx @@ -0,0 +1,1459 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: formulabase.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/formulabase.hxx" +#include <map> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/ReferenceFlags.hpp> +#include <com/sun/star/sheet/SingleReference.hpp> +#include <com/sun/star/sheet/ComplexReference.hpp> +#include <com/sun/star/sheet/FormulaLanguage.hpp> +#include <com/sun/star/sheet/FormulaMapGroup.hpp> +#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp> +#include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp> +#include <com/sun/star/sheet/XFormulaTokens.hpp> +#include "oox/helper/containerhelper.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/core/filterbase.hxx" +#include "oox/xls/biffinputstream.hxx" + +using ::rtl::OString; +using ::rtl::OStringBuffer; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OStringToOUString; +using ::rtl::OUStringToOString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::sheet::SingleReference; +using ::com::sun::star::sheet::ComplexReference; +using ::com::sun::star::sheet::FormulaToken; +using ::com::sun::star::sheet::FormulaOpCodeMapEntry; +using ::com::sun::star::sheet::XSpreadsheetDocument; +using ::com::sun::star::sheet::XFormulaOpCodeMapper; +using ::com::sun::star::sheet::XFormulaTokens; +using namespace ::com::sun::star::sheet::ReferenceFlags; + +namespace oox { +namespace xls { + +// reference helpers ========================================================== + +BinSingleRef2d::BinSingleRef2d() : + mnCol( 0 ), + mnRow( 0 ), + mbColRel( false ), + mbRowRel( false ) +{ +} + +void BinSingleRef2d::setOobData( sal_uInt16 nCol, sal_Int32 nRow, bool bRelativeAsOffset ) +{ + mnCol = nCol & OOBIN_TOK_REF_COLMASK; + mnRow = nRow & OOBIN_TOK_REF_ROWMASK; + mbColRel = getFlag( nCol, OOBIN_TOK_REF_COLREL ); + mbRowRel = getFlag( nCol, OOBIN_TOK_REF_ROWREL ); + if( bRelativeAsOffset && mbColRel && (mnCol > (OOBIN_TOK_REF_COLMASK >> 1)) ) + mnCol -= (OOBIN_TOK_REF_COLMASK + 1); + if( bRelativeAsOffset && mbRowRel && (mnRow > (OOBIN_TOK_REF_ROWMASK >> 1)) ) + mnRow -= (OOBIN_TOK_REF_ROWMASK + 1); +} + +void BinSingleRef2d::setBiff2Data( sal_uInt8 nCol, sal_uInt16 nRow, bool bRelativeAsOffset ) +{ + mnCol = nCol; + mnRow = nRow & BIFF_TOK_REF_ROWMASK; + mbColRel = getFlag( nRow, BIFF_TOK_REF_COLREL ); + mbRowRel = getFlag( nRow, BIFF_TOK_REF_ROWREL ); + if( bRelativeAsOffset && mbColRel && (mnCol >= 0x80) ) + mnCol -= 0x100; + if( bRelativeAsOffset && mbRowRel && (mnRow > (BIFF_TOK_REF_ROWMASK >> 1)) ) + mnRow -= (BIFF_TOK_REF_ROWMASK + 1); +} + +void BinSingleRef2d::setBiff8Data( sal_uInt16 nCol, sal_uInt16 nRow, bool bRelativeAsOffset ) +{ + mnCol = nCol & BIFF_TOK_REF_COLMASK; + mnRow = nRow; + mbColRel = getFlag( nCol, BIFF_TOK_REF_COLREL ); + mbRowRel = getFlag( nCol, BIFF_TOK_REF_ROWREL ); + if( bRelativeAsOffset && mbColRel && (mnCol > (BIFF_TOK_REF_COLMASK >> 1)) ) + mnCol -= (BIFF_TOK_REF_COLMASK + 1); + if( bRelativeAsOffset && mbRowRel && (mnRow >= 0x8000) ) + mnRow -= 0x10000; +} + +void BinSingleRef2d::readOobData( RecordInputStream& rStrm, bool bRelativeAsOffset ) +{ + sal_Int32 nRow; + sal_uInt16 nCol; + rStrm >> nRow >> nCol; + setOobData( nCol, nRow, bRelativeAsOffset ); +} + +void BinSingleRef2d::readBiff2Data( BiffInputStream& rStrm, bool bRelativeAsOffset ) +{ + sal_uInt16 nRow; + sal_uInt8 nCol; + rStrm >> nRow >> nCol; + setBiff2Data( nCol, nRow, bRelativeAsOffset ); +} + +void BinSingleRef2d::readBiff8Data( BiffInputStream& rStrm, bool bRelativeAsOffset ) +{ + sal_uInt16 nRow, nCol; + rStrm >> nRow >> nCol; + setBiff8Data( nCol, nRow, bRelativeAsOffset ); +} + +// ---------------------------------------------------------------------------- + +void BinComplexRef2d::readOobData( RecordInputStream& rStrm, bool bRelativeAsOffset ) +{ + sal_Int32 nRow1, nRow2; + sal_uInt16 nCol1, nCol2; + rStrm >> nRow1 >> nRow2 >> nCol1 >> nCol2; + maRef1.setOobData( nCol1, nRow1, bRelativeAsOffset ); + maRef2.setOobData( nCol2, nRow2, bRelativeAsOffset ); +} + +void BinComplexRef2d::readBiff2Data( BiffInputStream& rStrm, bool bRelativeAsOffset ) +{ + sal_uInt16 nRow1, nRow2; + sal_uInt8 nCol1, nCol2; + rStrm >> nRow1 >> nRow2 >> nCol1 >> nCol2; + maRef1.setBiff2Data( nCol1, nRow1, bRelativeAsOffset ); + maRef2.setBiff2Data( nCol2, nRow2, bRelativeAsOffset ); +} + +void BinComplexRef2d::readBiff8Data( BiffInputStream& rStrm, bool bRelativeAsOffset ) +{ + sal_uInt16 nRow1, nRow2, nCol1, nCol2; + rStrm >> nRow1 >> nRow2 >> nCol1 >> nCol2; + maRef1.setBiff8Data( nCol1, nRow1, bRelativeAsOffset ); + maRef2.setBiff8Data( nCol2, nRow2, bRelativeAsOffset ); +} + +// function data ============================================================== + +namespace { + +const size_t FUNCINFO_CLASSCOUNT = 5; /// Number of token class entries. + +const sal_uInt8 FUNCFLAG_VOLATILE = 0x01; /// Result is volatile (e.g. NOW() function). +const sal_uInt8 FUNCFLAG_IMPORTONLY = 0x02; /// Only used in import filter. +const sal_uInt8 FUNCFLAG_EXPORTONLY = 0x04; /// Only used in export filter. +const sal_uInt8 FUNCFLAG_MACROCALL = 0x08; /// Function is simulated by macro call in Excel. +const sal_uInt8 FUNCFLAG_EXTERNAL = 0x10; /// Function is external in Calc. + +typedef ::boost::shared_ptr< FunctionInfo > FunctionInfoRef; + +struct FunctionData +{ + const sal_Char* mpcOdfFuncName; /// ODF function name. + const sal_Char* mpcOoxFuncName; /// OOXML function name. + sal_uInt16 mnOobFuncId; /// OOBIN function identifier. + sal_uInt16 mnBiffFuncId; /// BIFF function identifier. + sal_uInt8 mnMinParamCount; /// Minimum number of parameters. + sal_uInt8 mnMaxParamCount; /// Maximum number of parameters. + sal_uInt8 mnRetClass; /// BIFF token class of the return value. + sal_uInt8 mpnParamClass[ FUNCINFO_CLASSCOUNT ]; /// Expected BIFF token classes of parameters. + sal_uInt8 mnFlags; /// Additional flags. + + inline bool isSupported( bool bImportFilter ) const; +}; + +inline bool FunctionData::isSupported( bool bImportFilter ) const +{ + /* For import filters: the FUNCFLAG_EXPORTONLY flag must not be set, + for export filters: the FUNCFLAG_IMPORTONLY flag must not be set. */ + return !getFlag( mnFlags, bImportFilter ? FUNCFLAG_EXPORTONLY : FUNCFLAG_IMPORTONLY ); +} + +const sal_uInt8 R = BIFF_TOKCLASS_REF; +const sal_uInt8 V = BIFF_TOKCLASS_VAL; +const sal_uInt8 A = BIFF_TOKCLASS_ARR; +const sal_uInt8 ER = FUNCINFO_PARAM_EXCELONLY | BIFF_TOKCLASS_REF; +const sal_uInt8 EV = FUNCINFO_PARAM_EXCELONLY | BIFF_TOKCLASS_VAL; +const sal_uInt8 EA = FUNCINFO_PARAM_EXCELONLY | BIFF_TOKCLASS_ARR; +const sal_uInt8 C = FUNCINFO_PARAM_CALCONLY; +const sal_uInt8 I = FUNCINFO_PARAM_INVALID; +const sal_uInt16 NOID = SAL_MAX_UINT16; +const sal_uInt8 MX = SAL_MAX_UINT8; + +/** Functions new in BIFF2. */ +static const FunctionData saFuncTableBiff2[] = +{ + { "COUNT", "COUNT", 0, 0, 0, MX, V, { R }, 0 }, + { "IF", "IF", 1, 1, 2, 3, R, { V, R }, 0 }, + { "ISNA", "ISNA", 2, 2, 1, 1, V, { V }, 0 }, + { "ISERROR", "ISERROR", 3, 3, 1, 1, V, { V }, 0 }, + { "SUM", "SUM", 4, 4, 0, MX, V, { R }, 0 }, + { "AVERAGE", "AVERAGE", 5, 5, 1, MX, V, { R }, 0 }, + { "MIN", "MIN", 6, 6, 1, MX, V, { R }, 0 }, + { "MAX", "MAX", 7, 7, 1, MX, V, { R }, 0 }, + { "ROW", "ROW", 8, 8, 0, 1, V, { R }, 0 }, + { "COLUMN", "COLUMN", 9, 9, 0, 1, V, { R }, 0 }, + { "NA", "NA", 10, 10, 0, 0, V, {}, 0 }, + { "NPV", "NPV", 11, 11, 2, MX, V, { V, R }, 0 }, + { "STDEV", "STDEV", 12, 12, 1, MX, V, { R }, 0 }, + { "DOLLAR", "DOLLAR", 13, 13, 1, 2, V, { V }, 0 }, + { "FIXED", "FIXED", 14, 14, 1, 2, V, { V, V, C, I }, 0 }, + { "SIN", "SIN", 15, 15, 1, 1, V, { V }, 0 }, + { "COS", "COS", 16, 16, 1, 1, V, { V }, 0 }, + { "TAN", "TAN", 17, 17, 1, 1, V, { V }, 0 }, + { "COT", "TAN", 17, 17, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "ATAN", "ATAN", 18, 18, 1, 1, V, { V }, 0 }, + { "ACOT", "ATAN", 18, 18, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "PI", "PI", 19, 19, 0, 0, V, {}, 0 }, + { "SQRT", "SQRT", 20, 20, 1, 1, V, { V }, 0 }, + { "EXP", "EXP", 21, 21, 1, 1, V, { V }, 0 }, + { "LN", "LN", 22, 22, 1, 1, V, { V }, 0 }, + { "LOG10", "LOG10", 23, 23, 1, 1, V, { V }, 0 }, + { "ABS", "ABS", 24, 24, 1, 1, V, { V }, 0 }, + { "INT", "INT", 25, 25, 1, 1, V, { V }, 0 }, + { "SIGN", "SIGN", 26, 26, 1, 1, V, { V }, 0 }, + { "ROUND", "ROUND", 27, 27, 2, 2, V, { V }, 0 }, + { "LOOKUP", "LOOKUP", 28, 28, 2, 3, V, { V, R }, 0 }, + { "INDEX", "INDEX", 29, 29, 2, 4, R, { R, V }, 0 }, + { "REPT", "REPT", 30, 30, 2, 2, V, { V }, 0 }, + { "MID", "MID", 31, 31, 3, 3, V, { V }, 0 }, + { "LEN", "LEN", 32, 32, 1, 1, V, { V }, 0 }, + { "VALUE", "VALUE", 33, 33, 1, 1, V, { V }, 0 }, + { "TRUE", "TRUE", 34, 34, 0, 0, V, {}, 0 }, + { "FALSE", "FALSE", 35, 35, 0, 0, V, {}, 0 }, + { "AND", "AND", 36, 36, 1, MX, V, { R }, 0 }, + { "OR", "OR", 37, 37, 1, MX, V, { R }, 0 }, + { "NOT", "NOT", 38, 38, 1, 1, V, { V }, 0 }, + { "MOD", "MOD", 39, 39, 2, 2, V, { V }, 0 }, + { "DCOUNT", "DCOUNT", 40, 40, 3, 3, V, { R }, 0 }, + { "DSUM", "DSUM", 41, 41, 3, 3, V, { R }, 0 }, + { "DAVERAGE", "DAVERAGE", 42, 42, 3, 3, V, { R }, 0 }, + { "DMIN", "DMIN", 43, 43, 3, 3, V, { R }, 0 }, + { "DMAX", "DMAX", 44, 44, 3, 3, V, { R }, 0 }, + { "DSTDEV", "DSTDEV", 45, 45, 3, 3, V, { R }, 0 }, + { "VAR", "VAR", 46, 46, 1, MX, V, { R }, 0 }, + { "DVAR", "DVAR", 47, 47, 3, 3, V, { R }, 0 }, + { "TEXT", "TEXT", 48, 48, 2, 2, V, { V }, 0 }, + { "LINEST", "LINEST", 49, 49, 1, 2, A, { R, R, C, C, I }, 0 }, + { "TREND", "TREND", 50, 50, 1, 3, A, { R, R, R, C, I }, 0 }, + { "LOGEST", "LOGEST", 51, 51, 1, 2, A, { R, R, C, C, I }, 0 }, + { "GROWTH", "GROWTH", 52, 52, 1, 3, A, { R, R, R, C, I }, 0 }, + { "PV", "PV", 56, 56, 3, 5, V, { V }, 0 }, + { "FV", "FV", 57, 57, 3, 5, V, { V }, 0 }, + { "NPER", "NPER", 58, 58, 3, 5, V, { V }, 0 }, + { "PMT", "PMT", 59, 59, 3, 5, V, { V }, 0 }, + { "RATE", "RATE", 60, 60, 3, 6, V, { V }, 0 }, + { "MIRR", "MIRR", 61, 61, 3, 3, V, { R, V }, 0 }, + { "IRR", "IRR", 62, 62, 1, 2, V, { R, V }, 0 }, + { "RAND", "RAND", 63, 63, 0, 0, V, {}, FUNCFLAG_VOLATILE }, + { "MATCH", "MATCH", 64, 64, 2, 3, V, { V, R }, 0 }, + { "DATE", "DATE", 65, 65, 3, 3, V, { V }, 0 }, + { "TIME", "TIME", 66, 66, 3, 3, V, { V }, 0 }, + { "DAY", "DAY", 67, 67, 1, 1, V, { V }, 0 }, + { "MONTH", "MONTH", 68, 68, 1, 1, V, { V }, 0 }, + { "YEAR", "YEAR", 69, 69, 1, 1, V, { V }, 0 }, + { "WEEKDAY", "WEEKDAY", 70, 70, 1, 1, V, { V, C, I }, 0 }, + { "HOUR", "HOUR", 71, 71, 1, 1, V, { V }, 0 }, + { "MINUTE", "MINUTE", 72, 72, 1, 1, V, { V }, 0 }, + { "SECOND", "SECOND", 73, 73, 1, 1, V, { V }, 0 }, + { "NOW", "NOW", 74, 74, 0, 0, V, {}, FUNCFLAG_VOLATILE }, + { "AREAS", "AREAS", 75, 75, 1, 1, V, { R }, 0 }, + { "ROWS", "ROWS", 76, 76, 1, 1, V, { R }, 0 }, + { "COLUMNS", "COLUMNS", 77, 77, 1, 1, V, { R }, 0 }, + { "OFFSET", "OFFSET", 78, 78, 3, 5, R, { R, V }, FUNCFLAG_VOLATILE }, + { "SEARCH", "SEARCH", 82, 82, 2, 3, V, { V }, 0 }, + { "TRANSPOSE", "TRANSPOSE", 83, 83, 1, 1, A, { A }, 0 }, + { "TYPE", "TYPE", 86, 86, 1, 1, V, { V }, 0 }, + { "ATAN2", "ATAN2", 97, 97, 2, 2, V, { V }, 0 }, + { "ASIN", "ASIN", 98, 98, 1, 1, V, { V }, 0 }, + { "ACOS", "ACOS", 99, 99, 1, 1, V, { V }, 0 }, + { "CHOOSE", "CHOOSE", 100, 100, 2, MX, R, { V, R }, 0 }, + { "HLOOKUP", "HLOOKUP", 101, 101, 3, 3, V, { V, R, R, C, I }, 0 }, + { "VLOOKUP", "VLOOKUP", 102, 102, 3, 3, V, { V, R, R, C, I }, 0 }, + { "ISREF", "ISREF", 105, 105, 1, 1, V, { R }, 0 }, + { "LOG", "LOG", 109, 109, 1, 2, V, { V }, 0 }, + { "CHAR", "CHAR", 111, 111, 1, 1, V, { V }, 0 }, + { "LOWER", "LOWER", 112, 112, 1, 1, V, { V }, 0 }, + { "UPPER", "UPPER", 113, 113, 1, 1, V, { V }, 0 }, + { "PROPER", "PROPER", 114, 114, 1, 1, V, { V }, 0 }, + { "LEFT", "LEFT", 115, 115, 1, 2, V, { V }, 0 }, + { "RIGHT", "RIGHT", 116, 116, 1, 2, V, { V }, 0 }, + { "EXACT", "EXACT", 117, 117, 2, 2, V, { V }, 0 }, + { "TRIM", "TRIM", 118, 118, 1, 1, V, { V }, 0 }, + { "REPLACE", "REPLACE", 119, 119, 4, 4, V, { V }, 0 }, + { "SUBSTITUTE", "SUBSTITUTE", 120, 120, 3, 4, V, { V }, 0 }, + { "CODE", "CODE", 121, 121, 1, 1, V, { V }, 0 }, + { "FIND", "FIND", 124, 124, 2, 3, V, { V }, 0 }, + { "CELL", "CELL", 125, 125, 1, 2, V, { V, R }, FUNCFLAG_VOLATILE }, + { "ISERR", "ISERR", 126, 126, 1, 1, V, { V }, 0 }, + { "ISTEXT", "ISTEXT", 127, 127, 1, 1, V, { V }, 0 }, + { "ISNUMBER", "ISNUMBER", 128, 128, 1, 1, V, { V }, 0 }, + { "ISBLANK", "ISBLANK", 129, 129, 1, 1, V, { V }, 0 }, + { "T", "T", 130, 130, 1, 1, V, { R }, 0 }, + { "N", "N", 131, 131, 1, 1, V, { R }, 0 }, + { "DATEVALUE", "DATEVALUE", 140, 140, 1, 1, V, { V }, 0 }, + { "TIMEVALUE", "TIMEVALUE", 141, 141, 1, 1, V, { V }, 0 }, + { "SLN", "SLN", 142, 142, 3, 3, V, { V }, 0 }, + { "SYD", "SYD", 143, 143, 4, 4, V, { V }, 0 }, + { "DDB", "DDB", 144, 144, 4, 5, V, { V }, 0 }, + { "INDIRECT", "INDIRECT", 148, 148, 1, 2, R, { V, EV, I }, FUNCFLAG_VOLATILE }, + { "CLEAN", "CLEAN", 162, 162, 1, 1, V, { V }, 0 }, + { "MDETERM", "MDETERM", 163, 163, 1, 1, V, { A }, 0 }, + { "MINVERSE", "MINVERSE", 164, 164, 1, 1, A, { A }, 0 }, + { "MMULT", "MMULT", 165, 165, 2, 2, A, { A }, 0 }, + { "IPMT", "IPMT", 167, 167, 4, 6, V, { V }, 0 }, + { "PPMT", "PPMT", 168, 168, 4, 6, V, { V }, 0 }, + { "COUNTA", "COUNTA", 169, 169, 0, MX, V, { R }, 0 }, + { "PRODUCT", "PRODUCT", 183, 183, 0, MX, V, { R }, 0 }, + { "FACT", "FACT", 184, 184, 1, 1, V, { V }, 0 }, + { "DPRODUCT", "DPRODUCT", 189, 189, 3, 3, V, { R }, 0 }, + { "ISNONTEXT", "ISNONTEXT", 190, 190, 1, 1, V, { V }, 0 }, + { "STDEVP", "STDEVP", 193, 193, 1, MX, V, { R }, 0 }, + { "VARP", "VARP", 194, 194, 1, MX, V, { R }, 0 }, + { "DSTDEVP", "DSTDEVP", 195, 195, 3, 3, V, { R }, 0 }, + { "DVARP", "DVARP", 196, 196, 3, 3, V, { R }, 0 }, + { "TRUNC", "TRUNC", 197, 197, 1, 1, V, { V, C, I }, 0 }, + { "ISLOGICAL", "ISLOGICAL", 198, 198, 1, 1, V, { V }, 0 }, + { "DCOUNTA", "DCOUNTA", 199, 199, 3, 3, V, { R }, 0 }, + { 0, 0, 255, 255, 1, MX, R, { ER, R }, FUNCFLAG_IMPORTONLY } // EXTERNAL +}; + +/** Functions new in BIFF3. */ +static const FunctionData saFuncTableBiff3[] = +{ + { "LINEST", "LINEST", 49, 49, 1, 4, A, { R, R, V, V }, 0 }, // BIFF2: 1-2, BIFF3: 1-4, + { "TREND", "TREND", 50, 50, 1, 4, A, { R, R, R, V }, 0 }, // BIFF2: 1-3, BIFF3: 1-4 + { "LOGEST", "LOGEST", 51, 51, 1, 4, A, { R, R, V, V }, 0 }, // BIFF2: 1-2, BIFF3: 1-4, + { "GROWTH", "GROWTH", 52, 52, 1, 4, A, { R, R, R, V }, 0 }, // BIFF2: 1-3, BIFF3: 1-4 + { "TRUNC", "TRUNC", 197, 197, 1, 2, V, { V }, 0 }, // BIFF2: 1, BIFF3: 1-2 + { "DOLLAR", "USDOLLAR", 204, 204, 1, 2, V, { V }, FUNCFLAG_IMPORTONLY }, + { 0/*"FIND"*/, "FINDB", 205, 205, 2, 3, V, { V }, 0 }, + { 0/*"SEARCH"*/, "SEARCHB", 206, 206, 2, 3, V, { V }, 0 }, + { 0/*"REPLACE"*/, "REPLACEB", 207, 207, 4, 4, V, { V }, 0 }, + { 0/*"LEFT"*/, "LEFTB", 208, 208, 1, 2, V, { V }, 0 }, + { 0/*"RIGHT"*/, "RIGHTB", 209, 209, 1, 2, V, { V }, 0 }, + { 0/*"MID"*/, "MIDB", 210, 210, 3, 3, V, { V }, 0 }, + { 0/*"LEN"*/, "LENB", 211, 211, 1, 1, V, { V }, 0 }, + { "ROUNDUP", "ROUNDUP", 212, 212, 2, 2, V, { V }, 0 }, + { "ROUNDDOWN", "ROUNDDOWN", 213, 213, 2, 2, V, { V }, 0 }, + { "ASC", "ASC", 214, 214, 1, 1, V, { V }, 0 }, + { "JIS", "DBCS", 215, 215, 1, 1, V, { V }, 0 }, + { "ADDRESS", "ADDRESS", 219, 219, 2, 5, V, { V, V, V, EV, V }, 0 }, + { "DAYS360", "DAYS360", 220, 220, 2, 2, V, { V, V, C, I }, 0 }, + { "TODAY", "TODAY", 221, 221, 0, 0, V, {}, FUNCFLAG_VOLATILE }, + { "VDB", "VDB", 222, 222, 5, 7, V, { V }, 0 }, + { "MEDIAN", "MEDIAN", 227, 227, 1, MX, V, { R }, 0 }, + { "SUMPRODUCT", "SUMPRODUCT", 228, 228, 1, MX, V, { A }, 0 }, + { "SINH", "SINH", 229, 229, 1, 1, V, { V }, 0 }, + { "COSH", "COSH", 230, 230, 1, 1, V, { V }, 0 }, + { "TANH", "TANH", 231, 231, 1, 1, V, { V }, 0 }, + { "COTH", "TANH", 231, 231, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "ASINH", "ASINH", 232, 232, 1, 1, V, { V }, 0 }, + { "ACOSH", "ACOSH", 233, 233, 1, 1, V, { V }, 0 }, + { "ATANH", "ATANH", 234, 234, 1, 1, V, { V }, 0 }, + { "ACOTH", "ATANH", 234, 234, 1, 1, V, { V }, FUNCFLAG_EXPORTONLY }, + { "DGET", "DGET", 235, 235, 3, 3, V, { R }, 0 }, + { "INFO", "INFO", 244, 244, 1, 1, V, { V }, FUNCFLAG_VOLATILE } +}; + +/** Functions new in BIFF4. */ +static const FunctionData saFuncTableBiff4[] = +{ + { "FIXED", "FIXED", 14, 14, 1, 3, V, { V }, 0 }, // BIFF2-3: 1-2, BIFF4: 1-3 + { "RANK", "RANK", 216, 216, 2, 3, V, { V, R, V }, 0 }, + { "DB", "DB", 247, 247, 4, 5, V, { V }, 0 }, + { "FREQUENCY", "FREQUENCY", 252, 252, 2, 2, A, { R }, 0 }, + { "ERROR.TYPE", "ERROR.TYPE", 261, 261, 1, 1, V, { V }, 0 }, + { "AVEDEV", "AVEDEV", 269, 269, 1, MX, V, { R }, 0 }, + { "BETADIST", "BETADIST", 270, 270, 3, 5, V, { V }, 0 }, + { "GAMMALN", "GAMMALN", 271, 271, 1, 1, V, { V }, 0 }, + { "BETAINV", "BETAINV", 272, 272, 3, 5, V, { V }, 0 }, + { "BINOMDIST", "BINOMDIST", 273, 273, 4, 4, V, { V }, 0 }, + { "LEGACY.CHIDIST", "CHIDIST", 274, 274, 2, 2, V, { V }, 0 }, + { "LEGACY.CHIINV", "CHIINV", 275, 275, 2, 2, V, { V }, 0 }, + { "COMBIN", "COMBIN", 276, 276, 2, 2, V, { V }, 0 }, + { "CONFIDENCE", "CONFIDENCE", 277, 277, 3, 3, V, { V }, 0 }, + { "CRITBINOM", "CRITBINOM", 278, 278, 3, 3, V, { V }, 0 }, + { "EVEN", "EVEN", 279, 279, 1, 1, V, { V }, 0 }, + { "EXPONDIST", "EXPONDIST", 280, 280, 3, 3, V, { V }, 0 }, + { "LEGACY.FDIST", "FDIST", 281, 281, 3, 3, V, { V }, 0 }, + { "LEGACY.FINV", "FINV", 282, 282, 3, 3, V, { V }, 0 }, + { "FISHER", "FISHER", 283, 283, 1, 1, V, { V }, 0 }, + { "FISHERINV", "FISHERINV", 284, 284, 1, 1, V, { V }, 0 }, + { "FLOOR", "FLOOR", 285, 285, 2, 2, V, { V, V, C, I }, 0 }, + { "GAMMADIST", "GAMMADIST", 286, 286, 4, 4, V, { V }, 0 }, + { "GAMMAINV", "GAMMAINV", 287, 287, 3, 3, V, { V }, 0 }, + { "CEILING", "CEILING", 288, 288, 2, 2, V, { V, V, C, I }, 0 }, + { "HYPGEOMDIST", "HYPGEOMDIST", 289, 289, 4, 4, V, { V }, 0 }, + { "LOGNORMDIST", "LOGNORMDIST", 290, 290, 3, 3, V, { V }, 0 }, + { "LOGINV", "LOGINV", 291, 291, 3, 3, V, { V }, 0 }, + { "NEGBINOMDIST", "NEGBINOMDIST", 292, 292, 3, 3, V, { V }, 0 }, + { "NORMDIST", "NORMDIST", 293, 293, 4, 4, V, { V }, 0 }, + { "LEGACY.NORMSDIST", "NORMSDIST", 294, 294, 1, 1, V, { V }, 0 }, + { "NORMINV", "NORMINV", 295, 295, 3, 3, V, { V }, 0 }, + { "LEGACY.NORMSINV", "NORMSINV", 296, 296, 1, 1, V, { V }, 0 }, + { "STANDARDIZE", "STANDARDIZE", 297, 297, 3, 3, V, { V }, 0 }, + { "ODD", "ODD", 298, 298, 1, 1, V, { V }, 0 }, + { "PERMUT", "PERMUT", 299, 299, 2, 2, V, { V }, 0 }, + { "POISSON", "POISSON", 300, 300, 3, 3, V, { V }, 0 }, + { "TDIST", "TDIST", 301, 301, 3, 3, V, { V }, 0 }, + { "WEIBULL", "WEIBULL", 302, 302, 4, 4, V, { V }, 0 }, + { "SUMXMY2", "SUMXMY2", 303, 303, 2, 2, V, { A }, 0 }, + { "SUMX2MY2", "SUMX2MY2", 304, 304, 2, 2, V, { A }, 0 }, + { "SUMX2PY2", "SUMX2PY2", 305, 305, 2, 2, V, { A }, 0 }, + { "LEGACY.CHITEST", "CHITEST", 306, 306, 2, 2, V, { A }, 0 }, + { "CORREL", "CORREL", 307, 307, 2, 2, V, { A }, 0 }, + { "COVAR", "COVAR", 308, 308, 2, 2, V, { A }, 0 }, + { "FORECAST", "FORECAST", 309, 309, 3, 3, V, { V, A }, 0 }, + { "FTEST", "FTEST", 310, 310, 2, 2, V, { A }, 0 }, + { "INTERCEPT", "INTERCEPT", 311, 311, 2, 2, V, { A }, 0 }, + { "PEARSON", "PEARSON", 312, 312, 2, 2, V, { A }, 0 }, + { "RSQ", "RSQ", 313, 313, 2, 2, V, { A }, 0 }, + { "STEYX", "STEYX", 314, 314, 2, 2, V, { A }, 0 }, + { "SLOPE", "SLOPE", 315, 315, 2, 2, V, { A }, 0 }, + { "TTEST", "TTEST", 316, 316, 4, 4, V, { A, A, V }, 0 }, + { "PROB", "PROB", 317, 317, 3, 4, V, { A, A, V }, 0 }, + { "DEVSQ", "DEVSQ", 318, 318, 1, MX, V, { R }, 0 }, + { "GEOMEAN", "GEOMEAN", 319, 319, 1, MX, V, { R }, 0 }, + { "HARMEAN", "HARMEAN", 320, 320, 1, MX, V, { R }, 0 }, + { "SUMSQ", "SUMSQ", 321, 321, 0, MX, V, { R }, 0 }, + { "KURT", "KURT", 322, 322, 1, MX, V, { R }, 0 }, + { "SKEW", "SKEW", 323, 323, 1, MX, V, { R }, 0 }, + { "ZTEST", "ZTEST", 324, 324, 2, 3, V, { R, V }, 0 }, + { "LARGE", "LARGE", 325, 325, 2, 2, V, { R, V }, 0 }, + { "SMALL", "SMALL", 326, 326, 2, 2, V, { R, V }, 0 }, + { "QUARTILE", "QUARTILE", 327, 327, 2, 2, V, { R, V }, 0 }, + { "PERCENTILE", "PERCENTILE", 328, 328, 2, 2, V, { R, V }, 0 }, + { "PERCENTRANK", "PERCENTRANK", 329, 329, 2, 3, V, { R, V, EV, I }, 0 }, + { "MODE", "MODE", 330, 330, 1, MX, V, { A }, 0 }, + { "TRIMMEAN", "TRIMMEAN", 331, 331, 2, 2, V, { R, V }, 0 }, + { "TINV", "TINV", 332, 332, 2, 2, V, { V }, 0 }, + + // *** Analysis add-in *** + + { "HEX2BIN", "HEX2BIN", 384, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "HEX2DEC", "HEX2DEC", 385, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "HEX2OCT", "HEX2OCT", 386, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "DEC2BIN", "DEC2BIN", 387, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "DEC2HEX", "DEC2HEX", 388, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "DEC2OCT", "DEC2OCT", 389, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "OCT2BIN", "OCT2BIN", 390, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "OCT2HEX", "OCT2HEX", 391, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "OCT2DEC", "OCT2DEC", 392, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "BIN2DEC", "BIN2DEC", 393, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "BIN2OCT", "BIN2OCT", 394, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "BIN2HEX", "BIN2HEX", 395, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMSUB", "IMSUB", 396, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMDIV", "IMDIV", 397, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMPOWER", "IMPOWER", 398, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMABS", "IMABS", 399, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMSQRT", "IMSQRT", 400, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMLN", "IMLN", 401, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMLOG2", "IMLOG2", 402, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMLOG10", "IMLOG10", 403, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMSIN", "IMSIN", 404, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMCOS", "IMCOS", 405, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMEXP", "IMEXP", 406, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMARGUMENT", "IMARGUMENT", 407, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMCONJUGATE", "IMCONJUGATE", 408, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMAGINARY", "IMAGINARY", 409, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMREAL", "IMREAL", 410, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "COMPLEX", "COMPLEX", 411, NOID, 2, 3, V, { V }, FUNCFLAG_EXTERNAL }, + { "IMSUM", "IMSUM", 412, NOID, 1, MX, V, { R }, FUNCFLAG_EXTERNAL }, + { "IMPRODUCT", "IMPRODUCT", 413, NOID, 1, MX, V, { R }, FUNCFLAG_EXTERNAL }, + { "SERIESSUM", "SERIESSUM", 414, NOID, 4, 4, V, { V, V, V, R }, FUNCFLAG_EXTERNAL }, + { "FACTDOUBLE", "FACTDOUBLE", 415, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "SQRTPI", "SQRTPI", 416, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "QUOTIENT", "QUOTIENT", 417, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "DELTA", "DELTA", 418, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "GESTEP", "GESTEP", 419, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "ISEVEN", "ISEVEN", 420, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "ISODD", "ISODD", 421, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "MROUND", "MROUND", 422, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "ERF", "ERF", 423, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "ERFC", "ERFC", 424, NOID, 1, 1, V, { V }, FUNCFLAG_EXTERNAL }, + { "BESSELJ", "BESSELJ", 425, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "BESSELK", "BESSELK", 426, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "BESSELY", "BESSELY", 427, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "BESSELI", "BESSELI", 428, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "XIRR", "XIRR", 429, NOID, 2, 3, V, { A, R, V }, FUNCFLAG_EXTERNAL }, + { "XNPV", "XNPV", 430, NOID, 3, 3, V, { V, A, R }, FUNCFLAG_EXTERNAL }, + { "PRICEMAT", "PRICEMAT", 431, NOID, 5, 6, V, { V }, FUNCFLAG_EXTERNAL }, + { "YIELDMAT", "YIELDMAT", 432, NOID, 5, 6, V, { V }, FUNCFLAG_EXTERNAL }, + { "INTRATE", "INTRATE", 433, NOID, 4, 5, V, { V }, FUNCFLAG_EXTERNAL }, + { "RECEIVED", "RECEIVED", 434, NOID, 4, 5, V, { V }, FUNCFLAG_EXTERNAL }, + { "DISC", "DISC", 435, NOID, 4, 5, V, { V }, FUNCFLAG_EXTERNAL }, + { "PRICEDISC", "PRICEDISC", 436, NOID, 4, 5, V, { V }, FUNCFLAG_EXTERNAL }, + { "YIELDDISC", "YIELDDISC", 437, NOID, 4, 5, V, { V }, FUNCFLAG_EXTERNAL }, + { "TBILLEQ", "TBILLEQ", 438, NOID, 3, 3, V, { V }, FUNCFLAG_EXTERNAL }, + { "TBILLPRICE", "TBILLPRICE", 439, NOID, 3, 3, V, { V }, FUNCFLAG_EXTERNAL }, + { "TBILLYIELD", "TBILLYIELD", 440, NOID, 3, 3, V, { V }, FUNCFLAG_EXTERNAL }, + { "PRICE", "PRICE", 441, NOID, 6, 7, V, { V }, FUNCFLAG_EXTERNAL }, + { "YIELD", "YIELD", 442, NOID, 6, 7, V, { V }, FUNCFLAG_EXTERNAL }, + { "DOLLARDE", "DOLLARDE", 443, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "DOLLARFR", "DOLLARFR", 444, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "NOMINAL", "NOMINAL", 445, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "EFFECT", "EFFECT", 446, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "CUMPRINC", "CUMPRINC", 447, NOID, 6, 6, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "CUMIPMT", "CUMIPMT", 448, NOID, 6, 6, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "EDATE", "EDATE", 449, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "EOMONTH", "EOMONTH", 450, NOID, 2, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "YEARFRAC", "YEARFRAC", 451, NOID, 2, 3, V, { V }, FUNCFLAG_EXTERNAL }, + { "COUPDAYBS", "COUPDAYBS", 452, NOID, 3, 4, V, { V }, FUNCFLAG_EXTERNAL }, + { "COUPDAYS", "COUPDAYS", 453, NOID, 3, 4, V, { V }, FUNCFLAG_EXTERNAL }, + { "COUPDAYSNC", "COUPDAYSNC", 454, NOID, 3, 4, V, { V }, FUNCFLAG_EXTERNAL }, + { "COUPNCD", "COUPNCD", 455, NOID, 3, 4, V, { V }, FUNCFLAG_EXTERNAL }, + { "COUPNUM", "COUPNUM", 456, NOID, 3, 4, V, { V }, FUNCFLAG_EXTERNAL }, + { "COUPPCD", "COUPPCD", 457, NOID, 3, 4, V, { V }, FUNCFLAG_EXTERNAL }, + { "DURATION", "DURATION", 458, NOID, 5, 6, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "MDURATION", "MDURATION", 459, NOID, 5, 6, V, { V }, FUNCFLAG_EXTERNAL }, + { "ODDLPRICE", "ODDLPRICE", 460, NOID, 7, 8, V, { V }, FUNCFLAG_EXTERNAL }, + { "ODDLYIELD", "ODDLYIELD", 461, NOID, 8, 9, V, { V }, FUNCFLAG_EXTERNAL }, + { "ODDFPRICE", "ODDFPRICE", 462, NOID, 8, 9, V, { V }, FUNCFLAG_EXTERNAL }, + { "ODDFYIELD", "ODDFYIELD", 463, NOID, 8, 9, V, { V }, FUNCFLAG_EXTERNAL }, + { "RANDBETWEEN", "RANDBETWEEN", 464, NOID, 2, 2, V, {}, FUNCFLAG_VOLATILE | FUNCFLAG_EXTERNAL }, + { "WEEKNUM", "WEEKNUM", 465, NOID, 1, 2, V, { V }, FUNCFLAG_EXTERNAL }, + { "AMORDEGRC", "AMORDEGRC", 466, NOID, 6, 7, V, { V }, FUNCFLAG_EXTERNAL }, + { "AMORLINC", "AMORLINC", 467, NOID, 6, 7, V, { V }, FUNCFLAG_EXTERNAL }, + { "CONVERT", "CONVERT", 468, NOID, 3, 3, V, { V }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "ACCRINT", "ACCRINT", 469, NOID, 6, 7, V, { V }, FUNCFLAG_EXTERNAL }, + { "ACCRINTM", "ACCRINTM", 470, NOID, 4, 5, V, { V }, FUNCFLAG_EXTERNAL }, + { "WORKDAY", "WORKDAY", 471, NOID, 2, 3, V, { V, V, A, C, I }, FUNCFLAG_EXTERNAL }, + { "NETWORKDAYS", "NETWORKDAYS", 472, NOID, 2, 3, V, { V, V, A, C, I }, FUNCFLAG_EXTERNAL }, + { "GCD", "GCD", 473, NOID, 1, MX, V, { R }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "MULTINOMIAL", "MULTINOMIAL", 474, NOID, 1, MX, V, { R }, FUNCFLAG_EXTERNAL }, + { "LCM", "LCM", 475, NOID, 1, MX, V, { R }, FUNCFLAG_EXTERNAL }, // Calc: builtin and add-in + { "FVSCHEDULE", "FVSCHEDULE", 476, NOID, 2, 2, V, { V, A }, FUNCFLAG_EXTERNAL } +// { "EUROCONVERT", "EUROCONVERT", NOID, NOID, 3, 5, V, { V }, FUNCFLAG_EXTERNAL }, // Euro conversion add-in +}; + +/** Functions new in BIFF5/BIFF7. */ +static const FunctionData saFuncTableBiff5[] = +{ + { "WEEKDAY", "WEEKDAY", 70, 70, 1, 2, V, { V }, 0 }, // BIFF2-4: 1, BIFF5: 1-2 + { "HLOOKUP", "HLOOKUP", 101, 101, 3, 4, V, { V, R, R, V }, 0 }, // BIFF2-4: 3, BIFF5: 3-4 + { "VLOOKUP", "VLOOKUP", 102, 102, 3, 4, V, { V, R, R, V }, 0 }, // BIFF2-4: 3, BIFF5: 3-4 + { "DAYS360", "DAYS360", 220, 220, 2, 3, V, { V }, 0 }, // BIFF3-4: 2, BIFF5: 2-3 + { 0, 0, 255, 255, 1, MX, R, { ER, R }, FUNCFLAG_EXPORTONLY }, // MACRO or EXTERNAL + { "CONCATENATE", "CONCATENATE", 336, 336, 0, MX, V, { V }, 0 }, + { "POWER", "POWER", 337, 337, 2, 2, V, { V }, 0 }, + { "RADIANS", "RADIANS", 342, 342, 1, 1, V, { V }, 0 }, + { "DEGREES", "DEGREES", 343, 343, 1, 1, V, { V }, 0 }, + { "SUBTOTAL", "SUBTOTAL", 344, 344, 2, MX, V, { V, R }, 0 }, + { "SUMIF", "SUMIF", 345, 345, 2, 3, V, { R, V, R }, 0 }, + { "COUNTIF", "COUNTIF", 346, 346, 2, 2, V, { R, V }, 0 }, + { "COUNTBLANK", "COUNTBLANK", 347, 347, 1, 1, V, { R }, 0 }, + { "ISPMT", "ISPMT", 350, 350, 4, 4, V, { V }, 0 }, + { 0, "DATEDIF", 351, 351, 3, 3, V, { V }, FUNCFLAG_IMPORTONLY }, // not supported in Calc + { 0, "DATESTRING", 352, 352, 1, 1, V, { V }, FUNCFLAG_IMPORTONLY }, // not supported in Calc, missing in OOX spec + { 0, "NUMBERSTRING", 353, 353, 2, 2, V, { V }, FUNCFLAG_IMPORTONLY }, // not supported in Calc, missing in OOX spec + { "ROMAN", "ROMAN", 354, 354, 1, 2, V, { V }, 0 } +}; + +/** Functions new in BIFF8. */ +static const FunctionData saFuncTableBiff8[] = +{ + { "GETPIVOTDATA", "GETPIVOTDATA", 358, 358, 2, MX, V, { V, R, V }, FUNCFLAG_IMPORTONLY }, + { "HYPERLINK", "HYPERLINK", 359, 359, 1, 2, V, { V }, 0 }, + { 0, "PHONETIC", 360, 360, 1, 1, V, { R }, FUNCFLAG_IMPORTONLY }, + { "AVERAGEA", "AVERAGEA", 361, 361, 1, MX, V, { R }, 0 }, + { "MAXA", "MAXA", 362, 362, 1, MX, V, { R }, 0 }, + { "MINA", "MINA", 363, 363, 1, MX, V, { R }, 0 }, + { "STDEVPA", "STDEVPA", 364, 364, 1, MX, V, { R }, 0 }, + { "VARPA", "VARPA", 365, 365, 1, MX, V, { R }, 0 }, + { "STDEVA", "STDEVA", 366, 366, 1, MX, V, { R }, 0 }, + { "VARA", "VARA", 367, 367, 1, MX, V, { R }, 0 }, + { "COM.MICROSOFT.BAHTTEXT", "BAHTTEXT", 368, 368, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAIDAYOFWEEK", 369, 369, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAIDIGIT", 370, 370, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAIMONTHOFYEAR", 371, 371, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAINUMSOUND", 372, 372, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAINUMSTRING", 373, 373, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAISTRINGLENGTH", 374, 374, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "ISTHAIDIGIT", 375, 375, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "ROUNDBAHTDOWN", 376, 376, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "ROUNDBAHTUP", 377, 377, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "THAIYEAR", 378, 378, 1, 1, V, { V }, FUNCFLAG_MACROCALL }, + { 0, "RTD", 379, 379, 3, 3, A, { V, V, R }, 0 } +}; + +/** Functions new in OOX. */ +static const FunctionData saFuncTableOox[] = +{ + { 0, "IFERROR", 480, NOID, 2, 2, V, { V, R }, 0 }, + { 0, "COUNTIFS", 481, NOID, 3, MX, V, { R, V }, 0 }, + { 0, "SUMIFS", 482, NOID, 3, MX, V, { R, V }, 0 }, + { 0, "AVERAGEIF", 483, NOID, 2, 3, V, { R, V, R }, 0 }, + { 0, "AVERAGEIFS", 484, NOID, 3, MX, V, { R, V }, 0 }, + { 0, "CUBEKPIMEMBER", NOID, NOID, 3, 4, V, { V }, 0 }, + { 0, "CUBEMEMBER", NOID, NOID, 2, 3, V, { V, A, V }, 0 }, + { 0, "CUBEMEMBERPROPERTY",NOID, NOID, 3, 3, V, { V }, 0 }, + { 0, "CUBERANKEDMEMBER", NOID, NOID, 3, 4, V, { V }, 0 }, + { 0, "CUBESET", NOID, NOID, 2, 5, V, { V, R, V }, 0 }, + { 0, "CUBESETCOUNT", NOID, NOID, 1, 1, V, { V }, 0 }, + { 0, "CUBEVALUE", NOID, NOID, 2, 2, V, { V, R }, 0 } +}; + +/** Functions defined by OpenFormula, but not supported by Calc or by Excel. */ +static const FunctionData saFuncTableOdf[] = +{ + { "ARABIC", 0, NOID, NOID, 1, 1, V, { V }, 0 }, + { "B", 0, NOID, NOID, 3, 4, V, { V }, 0 }, + { "BASE", 0, NOID, NOID, 2, 3, V, { V }, 0 }, + { "BITAND", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "BITLSHIFT", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "BITOR", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "BITRSHIFT", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "BITXOR", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "CHISQDIST", 0, NOID, NOID, 2, 3, V, { V }, 0 }, + { "CHISQINV", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "COMBINA", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "DAYS", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "DDE", 0, NOID, NOID, 3, 4, V, { V }, 0 }, + { "DECIMAL", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "FDIST", 0, NOID, NOID, 3, 4, V, { V }, 0 }, + { "FINV", 0, NOID, NOID, 3, 3, V, { V }, 0 }, + { "FORMULA", 0, NOID, NOID, 1, 1, V, { R }, 0 }, + { "GAMMA", 0, NOID, NOID, 1, 1, V, { V }, 0 }, + { "GAUSS", 0, NOID, NOID, 1, 1, V, { V }, 0 }, + { "IFNA", 0, NOID, NOID, 2, 2, V, { V, R }, 0 }, + { "ISFORMULA", 0, NOID, NOID, 1, 1, V, { R }, 0 }, + { "ISOWEEKNUM", 0, NOID, NOID, 1, 2, V, { V }, 0 }, + { "MULTIPLE.OPERATIONS", 0, NOID, NOID, 3, 5, V, { R }, 0 }, + { "MUNIT", 0, NOID, NOID, 1, 1, A, { V }, 0 }, + { "NUMBERVALUE", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "PDURATION", 0, NOID, NOID, 3, 3, V, { V }, 0 }, + { "PERMUTATIONA", 0, NOID, NOID, 2, 2, V, { V }, 0 }, + { "PHI", 0, NOID, NOID, 1, 1, V, { V }, 0 }, + { "RRI", 0, NOID, NOID, 3, 3, V, { V }, 0 }, + { "SHEET", 0, NOID, NOID, 1, 1, V, { R }, 0 }, + { "SHEETS", 0, NOID, NOID, 0, 1, V, { R }, 0 }, + { "SKEWP", 0, NOID, NOID, 1, MX, V, { R }, 0 }, + { "UNICHAR", 0, NOID, NOID, 1, 1, V, { V }, 0 }, + { "UNICODE", 0, NOID, NOID, 1, 1, V, { V }, 0 }, + { "XOR", 0, NOID, NOID, 1, MX, V, { R }, 0 } +}; + +} // namespace + +// function info parameter class iterator ===================================== + +FuncInfoParamClassIterator::FuncInfoParamClassIterator( const FunctionInfo& rFuncInfo ) : + mpnParamClass( rFuncInfo.mpnParamClass ), + mpnParamClassEnd( rFuncInfo.mpnParamClass + FUNCINFO_CLASSCOUNT ) +{ +} + +FuncInfoParamClassIterator& FuncInfoParamClassIterator::operator++() +{ + if( (mpnParamClass + 1 < mpnParamClassEnd) && (mpnParamClass[ 1 ] != 0) ) + ++mpnParamClass; + return *this; +} + +// function provider implementation =========================================== + +class FunctionProviderImpl +{ +public: + explicit FunctionProviderImpl( + ApiOpCodes& rOpCodes, + const Reference< XSpreadsheetDocument >& rxDocument, + bool bImportFilter ); + + explicit FunctionProviderImpl( + ApiOpCodes& rOpCodes, + const Reference< XSpreadsheetDocument >& rxDocument, + BiffType eBiff, + bool bImportFilter ); + + const FunctionInfo* getFuncInfoFromApiToken( const ApiToken& rToken ) const; + const FunctionInfo* getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const; + const FunctionInfo* getFuncInfoFromOobFuncId( sal_uInt16 nFuncId ) const; + const FunctionInfo* getFuncInfoFromBiffFuncId( sal_uInt16 nFuncId ) const; + const FunctionInfo* getFuncInfoFromExternCallName( const OUString& rExtCallName ) const; + + Sequence< FormulaOpCodeMapEntry > getOoxParserMap() const; + +private: + typedef ::std::map< OUString, FormulaToken > FormulaTokenMap; + typedef Sequence< FormulaOpCodeMapEntry > OpCodeEntrySequence; + typedef ::std::vector< FormulaOpCodeMapEntry > OpCodeEntryVector; + + static bool fillEntrySeq( OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ); + static bool fillTokenMap( FormulaTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ); + + static bool initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId ); + static bool initOpCode( OpCodeEntryVector& orParserMap, sal_Int32& ornOpCode, const FormulaTokenMap& rTokenMap, const sal_Char* pcOdfName, const sal_Char* pcOoxName ); + + void construct( const Reference< XSpreadsheetDocument >& rxDocument, bool bImportFilter ); + void construct( const Reference< XSpreadsheetDocument >& rxDocument, BiffType eBiff, bool bImportFilter ); + + bool initFuncNames( const OpCodeEntrySequence& rEntrySeq ); + void initOpCodes( const Reference< XSpreadsheetDocument >& rxDocument ); + + void initFuncOpCode( FunctionInfo& orFuncInfo, const FormulaTokenMap& rFuncTokens ); + void initFuncMaps( const FunctionData* pBeg, const FunctionData* pEnd ); + +private: + typedef RefMap< sal_Int32, FunctionInfo > OpCodeFuncMap; + typedef RefMap< OUString, FunctionInfo > FuncNameMap; + typedef RefMap< sal_uInt16, FunctionInfo > FuncIdMap; + + ApiOpCodes& mrOpCodes; /// All needed API op-codes. + FormulaTokenMap maIntFuncTokens; /// Internal functions keyed by ODFF name. + FormulaTokenMap maExtFuncTokens; /// External functions keyed by ODFF name. + OpCodeFuncMap maOpCodeFuncs; /// Maps API op-codes to function data. + FuncNameMap maOoxFuncs; /// Maps OOXML function names to function data. + FuncNameMap maExtProgFuncs; /// Maps programmatical API function names to function data. + FuncIdMap maOobFuncs; /// Maps OOBIN function indexes to function data. + FuncIdMap maBiffFuncs; /// Maps BIFF function indexes to function data. + FuncNameMap maMacroFuncs; /// Maps BIFF macro function names to function data. + OpCodeEntryVector maParserMap; /// OOXML token mapping for formula parser service. + sal_uInt8 mnMaxParam; /// Maximum parameter count for current file type. + bool mbImportFilter; /// True = import filter, false = export filter. +}; + +// ---------------------------------------------------------------------------- + +FunctionProviderImpl::FunctionProviderImpl( ApiOpCodes& rOpCodes, + const Reference< XSpreadsheetDocument >& rxDocument, bool bImportFilter ) : + mrOpCodes( rOpCodes ), + mnMaxParam( OOX_MAX_PARAMCOUNT ) +{ + construct( rxDocument, bImportFilter ); +} + +FunctionProviderImpl::FunctionProviderImpl( ApiOpCodes& rOpCodes, + const Reference< XSpreadsheetDocument >& rxDocument, BiffType eBiff, bool bImportFilter ) : + mrOpCodes( rOpCodes ), + mnMaxParam( BIFF_MAX_PARAMCOUNT ) +{ + construct( rxDocument, eBiff, bImportFilter ); +} + +const FunctionInfo* FunctionProviderImpl::getFuncInfoFromApiToken( const ApiToken& rToken ) const +{ + const FunctionInfo* pFuncInfo = 0; + if( (rToken.OpCode == mrOpCodes.OPCODE_EXTERNAL) && rToken.Data.hasValue() ) + { + OUString aProgFuncName; + if( rToken.Data >>= aProgFuncName ) + pFuncInfo = maExtProgFuncs.get( aProgFuncName ).get(); + } + else + { + pFuncInfo = maOpCodeFuncs.get( rToken.OpCode ).get(); + } + return pFuncInfo; +} + +const FunctionInfo* FunctionProviderImpl::getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const +{ + return maOoxFuncs.get( rFuncName ).get(); +} + +const FunctionInfo* FunctionProviderImpl::getFuncInfoFromOobFuncId( sal_uInt16 nFuncId ) const +{ + return maOobFuncs.get( nFuncId ).get(); +} + +const FunctionInfo* FunctionProviderImpl::getFuncInfoFromBiffFuncId( sal_uInt16 nFuncId ) const +{ + return maBiffFuncs.get( nFuncId ).get(); +} + +const FunctionInfo* FunctionProviderImpl::getFuncInfoFromExternCallName( const OUString& rExtCallName ) const +{ + return maMacroFuncs.get( rExtCallName ).get(); +} + +Sequence< FormulaOpCodeMapEntry > FunctionProviderImpl::getOoxParserMap() const +{ + return ContainerHelper::vectorToSequence( maParserMap ); +} + +// private -------------------------------------------------------------------- + +bool FunctionProviderImpl::fillEntrySeq( OpCodeEntrySequence& orEntrySeq, + const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ) +{ + try + { + orEntrySeq = rxMapper->getAvailableMappings( ::com::sun::star::sheet::FormulaLanguage::ODFF, nMapGroup ); + return orEntrySeq.hasElements(); + } + catch( Exception& ) + { + } + return false; +} + +bool FunctionProviderImpl::fillTokenMap( FormulaTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, + const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup ) +{ + orTokenMap.clear(); + if( fillEntrySeq( orEntrySeq, rxMapper, nMapGroup ) ) + { + const FormulaOpCodeMapEntry* pEntry = orEntrySeq.getConstArray(); + const FormulaOpCodeMapEntry* pEntryEnd = pEntry + orEntrySeq.getLength(); + for( ; pEntry != pEntryEnd; ++pEntry ) + orTokenMap[ pEntry->Name ] = pEntry->Token; + } + return !orTokenMap.empty(); +} + +bool FunctionProviderImpl::initOpCode( sal_Int32& ornOpCode, + const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId ) +{ + if( (0 <= nSpecialId) && (nSpecialId < rEntrySeq.getLength()) ) + { + ornOpCode = rEntrySeq[ nSpecialId ].Token.OpCode; + return true; + } + OSL_ENSURE( false, + OStringBuffer( "FunctionProviderImpl::initOpCode - opcode for special offset " ). + append( nSpecialId ).append( " not found" ).getStr() ); + return false; +} + +bool FunctionProviderImpl::initOpCode( OpCodeEntryVector& orParserMap, sal_Int32& ornOpCode, + const FormulaTokenMap& rTokenMap, const sal_Char* pcOdfName, const sal_Char* pcOoxName ) +{ + OUString aOdfName = OUString::createFromAscii( pcOdfName ); + FormulaTokenMap::const_iterator aIt = rTokenMap.find( aOdfName ); + if( aIt != rTokenMap.end() ) + { + ornOpCode = aIt->second.OpCode; + if( pcOoxName ) + { + FormulaOpCodeMapEntry aEntry; + aEntry.Name = OUString::createFromAscii( pcOoxName ); + aEntry.Token.OpCode = ornOpCode; + orParserMap.push_back( aEntry ); + } + return true; + } + OSL_ENSURE( false, + OStringBuffer( "FunctionProviderImpl::initOpCode - opcode for \"" ). + append( OUStringToOString( aOdfName, RTL_TEXTENCODING_ASCII_US ) ). + append( "\" not found" ).getStr() ); + return false; +} + +void FunctionProviderImpl::construct( + const Reference< XSpreadsheetDocument >& rxDocument, bool bImportFilter ) +{ + construct( rxDocument, BIFF8, bImportFilter ); + // additional functions for OOX + initFuncMaps( saFuncTableOox, STATIC_ARRAY_END( saFuncTableOox ) ); +} + +void FunctionProviderImpl::construct( + const Reference< XSpreadsheetDocument >& rxDocument, BiffType eBiff, bool bImportFilter ) +{ + mbImportFilter = bImportFilter; + OSL_ENSURE( mbImportFilter, "FunctionProviderImpl::construct - need special handling for macro call functions" ); + + // operator op-codes, special op-codes, function op-codes + initOpCodes( rxDocument ); + + /* Add functions supported in the current BIFF version only. + Function tables from later BIFF versions may overwrite single + functions from earlier tables. */ + if( eBiff >= BIFF2 ) + initFuncMaps( saFuncTableBiff2, STATIC_ARRAY_END( saFuncTableBiff2 ) ); + if( eBiff >= BIFF3 ) + initFuncMaps( saFuncTableBiff3, STATIC_ARRAY_END( saFuncTableBiff3 ) ); + if( eBiff >= BIFF4 ) + initFuncMaps( saFuncTableBiff4, STATIC_ARRAY_END( saFuncTableBiff4 ) ); + if( eBiff >= BIFF5 ) + initFuncMaps( saFuncTableBiff5, STATIC_ARRAY_END( saFuncTableBiff5 ) ); + if( eBiff >= BIFF8 ) + initFuncMaps( saFuncTableBiff8, STATIC_ARRAY_END( saFuncTableBiff8 ) ); +} + +bool FunctionProviderImpl::initFuncNames( const OpCodeEntrySequence& rEntrySeq ) +{ + const FormulaOpCodeMapEntry* pEntry = rEntrySeq.getConstArray(); + const FormulaOpCodeMapEntry* pEntryEnd = pEntry + rEntrySeq.getLength(); + for( ; pEntry != pEntryEnd; ++pEntry ) + { + if( pEntry->Token.OpCode == mrOpCodes.OPCODE_EXTERNAL ) + maExtFuncTokens[ pEntry->Name ] = pEntry->Token; + else + maIntFuncTokens[ pEntry->Name ] = pEntry->Token; + } + return true; +} + +void FunctionProviderImpl::initOpCodes( const Reference< XSpreadsheetDocument >& rxDocument ) +{ + bool bIsValid = false; + try + { + Reference< XMultiServiceFactory > xFactory( rxDocument, UNO_QUERY_THROW ); + Reference< XFormulaOpCodeMapper > xMapper( xFactory->createInstance( + CREATE_OUSTRING( "com.sun.star.sheet.FormulaOpCodeMapper" ) ), UNO_QUERY_THROW ); + + // op-codes provided as attributes + mrOpCodes.OPCODE_EXTERNAL = xMapper->getOpCodeExternal(); + mrOpCodes.OPCODE_UNKNOWN = xMapper->getOpCodeUnknown(); + + using namespace ::com::sun::star::sheet::FormulaMapGroup; + using namespace ::com::sun::star::sheet::FormulaMapGroupSpecialOffset; + + OpCodeEntrySequence aEntrySeq; + FormulaTokenMap aTokenMap; + bIsValid = + // special + fillEntrySeq( aEntrySeq, xMapper, SPECIAL ) && + initOpCode( mrOpCodes.OPCODE_PUSH, aEntrySeq, PUSH ) && + initOpCode( mrOpCodes.OPCODE_MISSING, aEntrySeq, MISSING ) && + initOpCode( mrOpCodes.OPCODE_SPACES, aEntrySeq, SPACES ) && + initOpCode( mrOpCodes.OPCODE_NAME, aEntrySeq, NAME ) && + initOpCode( mrOpCodes.OPCODE_DBAREA, aEntrySeq, DB_AREA ) && + initOpCode( mrOpCodes.OPCODE_NLR, aEntrySeq, COL_ROW_NAME ) && + initOpCode( mrOpCodes.OPCODE_MACRO, aEntrySeq, MACRO ) && + initOpCode( mrOpCodes.OPCODE_BAD, aEntrySeq, BAD ) && + initOpCode( mrOpCodes.OPCODE_NONAME, aEntrySeq, NO_NAME ) && + // separators + fillTokenMap( aTokenMap, aEntrySeq, xMapper, SEPARATORS ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_OPEN, aTokenMap, "(", "(" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_CLOSE, aTokenMap, ")", ")" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_SEP, aTokenMap, ";", "," ) && + // array separators + fillTokenMap( aTokenMap, aEntrySeq, xMapper, ARRAY_SEPARATORS ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_ARRAY_OPEN, aTokenMap, "{", "{" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_ARRAY_CLOSE, aTokenMap, "}", "}" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_ARRAY_ROWSEP, aTokenMap, "|", ";" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_ARRAY_COLSEP, aTokenMap, ";", "," ) && + // unary operators + fillTokenMap( aTokenMap, aEntrySeq, xMapper, UNARY_OPERATORS ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_PLUS_SIGN, aTokenMap, "+", 0 ) && // same op-code as OPCODE_ADD + initOpCode( maParserMap, mrOpCodes.OPCODE_MINUS_SIGN, aTokenMap, "-", "-" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_PERCENT, aTokenMap, "%", "%" ) && + // binary operators + fillTokenMap( aTokenMap, aEntrySeq, xMapper, BINARY_OPERATORS ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_ADD, aTokenMap, "+", "+" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_SUB, aTokenMap, "-", "-" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_MULT, aTokenMap, "*", "*" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_DIV, aTokenMap, "/", "/" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_POWER, aTokenMap, "^", "^" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_CONCAT, aTokenMap, "&", "&" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_EQUAL, aTokenMap, "=", "=" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_NOT_EQUAL, aTokenMap, "<>", "<>" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_LESS, aTokenMap, "<", "<" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_LESS_EQUAL, aTokenMap, "<=", "<=" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_GREATER, aTokenMap, ">", ">" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_GREATER_EQUAL, aTokenMap, ">=", ">=" ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_INTERSECT, aTokenMap, "!", " " ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_LIST, aTokenMap, "~", "," ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_RANGE, aTokenMap, ":", ":" ) && + // functions + fillTokenMap( aTokenMap, aEntrySeq, xMapper, FUNCTIONS ) && + initFuncNames( aEntrySeq ) && + initOpCode( maParserMap, mrOpCodes.OPCODE_DDE, aTokenMap, "DDE", 0 ); + + // OPCODE_PLUS_SIGN and OPCODE_ADD should be equal, otherwise "+" has to be passed above + OSL_ENSURE( mrOpCodes.OPCODE_PLUS_SIGN == mrOpCodes.OPCODE_ADD, + "FunctionProviderImpl::initOpCodes - need opcode mapping for OPCODE_PLUS_SIGN" ); + // OPCODE_LIST not supported in Calc core + mrOpCodes.OPCODE_LIST = mrOpCodes.OPCODE_SEP; + } + catch( Exception& ) + { + } + OSL_ENSURE( bIsValid, "FunctionProviderImpl::initOpCodes - opcodes not initialized" ); +} + +void FunctionProviderImpl::initFuncOpCode( FunctionInfo& orFuncInfo, const FormulaTokenMap& rFuncTokens ) +{ + bool bRet = false; + if( orFuncInfo.mnOobFuncId == OOBIN_FUNC_EXTERNCALL ) + { + orFuncInfo.mnApiOpCode = mrOpCodes.OPCODE_EXTERNAL; + bRet = true; + } + else if( orFuncInfo.maOdfFuncName.getLength() > 0 ) + { + FormulaTokenMap::const_iterator aIt = rFuncTokens.find( orFuncInfo.maOdfFuncName ); + if( aIt != rFuncTokens.end() ) + { + orFuncInfo.mnApiOpCode = aIt->second.OpCode; + OSL_ENSURE( orFuncInfo.mnApiOpCode != mrOpCodes.OPCODE_NONAME, + OStringBuffer( "FunctionProviderImpl::initFuncOpCode - no valid op-code for \"" ). + append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ). + append( '"' ).getStr() ); + OSL_ENSURE( orFuncInfo.maOoxFuncName.getLength() > 0, + OStringBuffer( "FunctionProviderImpl::initFuncOpCode - no valid OOX function name for \"" ). + append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ). + append( '"' ).getStr() ); + bRet = (orFuncInfo.mnApiOpCode != mrOpCodes.OPCODE_EXTERNAL) || + ((aIt->second.Data >>= orFuncInfo.maExtProgName) && (orFuncInfo.maExtProgName.getLength() > 0)); + if( bRet ) + { + // create the parser map entry + FormulaOpCodeMapEntry aEntry; + aEntry.Name = orFuncInfo.maOoxFuncName; + aEntry.Token = aIt->second; + maParserMap.push_back( aEntry ); + } + } + OSL_ENSURE( bRet, + OStringBuffer( "FunctionProviderImpl::initFuncOpCode - opcode or external name for \"" ). + append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ). + append( "\" not found" ).getStr() ); + } + if( !bRet || (orFuncInfo.mnApiOpCode == mrOpCodes.OPCODE_UNKNOWN) ) + orFuncInfo.mnApiOpCode = mrOpCodes.OPCODE_NONAME; +} + +void FunctionProviderImpl::initFuncMaps( const FunctionData* pBeg, const FunctionData* pEnd ) +{ + for( const FunctionData* pIt = pBeg; pIt != pEnd; ++pIt ) + { + if( pIt->isSupported( mbImportFilter ) ) + { + // create a function info object + FunctionInfoRef xFuncInfo( new FunctionInfo ); + if( pIt->mpcOdfFuncName ) + xFuncInfo->maOdfFuncName = OUString::createFromAscii( pIt->mpcOdfFuncName ); + if( pIt->mpcOoxFuncName ) + xFuncInfo->maOoxFuncName = OUString::createFromAscii( pIt->mpcOoxFuncName ); + if( getFlag( pIt->mnFlags, FUNCFLAG_MACROCALL ) ) + xFuncInfo->maExternCallName = CREATE_OUSTRING( "_xlfn." ) + xFuncInfo->maOoxFuncName; + else if( getFlag( pIt->mnFlags, FUNCFLAG_EXTERNAL ) ) + xFuncInfo->maExternCallName = xFuncInfo->maOoxFuncName; + xFuncInfo->mnOobFuncId = pIt->mnOobFuncId; + xFuncInfo->mnBiffFuncId = pIt->mnBiffFuncId; + xFuncInfo->mnMinParamCount = pIt->mnMinParamCount; + xFuncInfo->mnMaxParamCount = (pIt->mnMaxParamCount == MX) ? mnMaxParam : pIt->mnMaxParamCount; + xFuncInfo->mnRetClass = pIt->mnRetClass; + xFuncInfo->mpnParamClass = pIt->mpnParamClass; + xFuncInfo->mbVolatile = getFlag( pIt->mnFlags, FUNCFLAG_VOLATILE ); + + // get API opcode from BIFF function index or function name + initFuncOpCode( *xFuncInfo, getFlag( pIt->mnFlags, FUNCFLAG_EXTERNAL ) ? maExtFuncTokens : maIntFuncTokens ); + + // insert the function info into the maps + if( xFuncInfo->mnApiOpCode != mrOpCodes.OPCODE_NONAME ) + { + if( (xFuncInfo->mnApiOpCode == mrOpCodes.OPCODE_EXTERNAL) && (xFuncInfo->maExtProgName.getLength() > 0) ) + maExtProgFuncs[ xFuncInfo->maExtProgName ] = xFuncInfo; + else + maOpCodeFuncs[ xFuncInfo->mnApiOpCode ] = xFuncInfo; + } + if( xFuncInfo->maOoxFuncName.getLength() > 0 ) + maOoxFuncs[ xFuncInfo->maOoxFuncName ] = xFuncInfo; + if( xFuncInfo->mnOobFuncId != NOID ) + maOobFuncs[ xFuncInfo->mnOobFuncId ] = xFuncInfo; + if( xFuncInfo->mnBiffFuncId != NOID ) + maBiffFuncs[ xFuncInfo->mnBiffFuncId ] = xFuncInfo; + if( xFuncInfo->maExternCallName.getLength() > 0 ) + maMacroFuncs[ xFuncInfo->maExternCallName ] = xFuncInfo; + } + } +} + +// function provider ========================================================== + +FunctionProvider::FunctionProvider( const WorkbookHelper& rHelper ) +{ + bool bImportFilter = rHelper.getBaseFilter().isImportFilter(); + switch( rHelper.getFilterType() ) + { + case FILTER_OOX: + mxImpl.reset( new FunctionProviderImpl( *this, rHelper.getDocument(), bImportFilter ) ); + break; + case FILTER_BIFF: + mxImpl.reset( new FunctionProviderImpl( *this, rHelper.getDocument(), rHelper.getBiff(), bImportFilter ) ); + break; + case FILTER_UNKNOWN: break; + } +} + +FunctionProvider::FunctionProvider( const Reference< XSpreadsheetDocument >& rxDocument, bool bImportFilter ) +{ + mxImpl.reset( new FunctionProviderImpl( *this, rxDocument, bImportFilter ) ); +} + +FunctionProvider::FunctionProvider( const Reference< XSpreadsheetDocument >& rxDocument, BiffType eBiff, bool bImportFilter ) +{ + mxImpl.reset( new FunctionProviderImpl( *this, rxDocument, eBiff, bImportFilter ) ); +} + +FunctionProvider::~FunctionProvider() +{ +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromApiToken( const ApiToken& rToken ) const +{ + return mxImpl->getFuncInfoFromApiToken( rToken ); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const +{ + return mxImpl->getFuncInfoFromOoxFuncName( rFuncName ); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromOobFuncId( sal_uInt16 nFuncId ) const +{ + return mxImpl->getFuncInfoFromOobFuncId( nFuncId ); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromBiffFuncId( sal_uInt16 nFuncId ) const +{ + return mxImpl->getFuncInfoFromBiffFuncId( nFuncId ); +} + +const FunctionInfo* FunctionProvider::getFuncInfoFromExternCallName( const OUString& rExtCallName ) const +{ + return mxImpl->getFuncInfoFromExternCallName( rExtCallName ); +} + +Sequence< FormulaOpCodeMapEntry > FunctionProvider::getOoxParserMap() const +{ + return mxImpl->getOoxParserMap(); +} + +// token sequence iterator ==================================================== + +ApiTokenIterator::ApiTokenIterator( const ApiTokenSequence& rTokens, sal_Int32 nSpacesOpCode, bool bSkipSpaces ) : + mpToken( rTokens.getConstArray() ), + mpTokenEnd( rTokens.getConstArray() + rTokens.getLength() ), + mnSpacesOpCode( nSpacesOpCode ), + mbSkipSpaces( bSkipSpaces ) +{ + skipSpaces(); +} + +ApiTokenIterator::ApiTokenIterator( const ApiTokenIterator& rIter, bool bSkipSpaces ) : + mpToken( rIter.mpToken ), + mpTokenEnd( rIter.mpTokenEnd ), + mnSpacesOpCode( rIter.mnSpacesOpCode ), + mbSkipSpaces( bSkipSpaces ) +{ + skipSpaces(); +} + +ApiTokenIterator& ApiTokenIterator::operator++() +{ + if( is() ) + { + ++mpToken; + skipSpaces(); + } + return *this; +} + +void ApiTokenIterator::skipSpaces() +{ + if( mbSkipSpaces ) + while( is() && (mpToken->OpCode == mnSpacesOpCode) ) + ++mpToken; +} + +// formual contexts =========================================================== + +FormulaContext::FormulaContext( bool bRelativeAsOffset, bool bAlways3dRefs ) : + maBaseAddress( 0, 0, 0 ), + mbRelativeAsOffset( bRelativeAsOffset ), + mbAlways3dRefs( bAlways3dRefs ) +{ +} + +FormulaContext::~FormulaContext() +{ +} + +void FormulaContext::setSharedFormula( const CellAddress& ) +{ +} + +// ---------------------------------------------------------------------------- + +TokensFormulaContext::TokensFormulaContext( bool bRelativeAsOffset, bool bAlways3dRefs ) : + FormulaContext( bRelativeAsOffset, bAlways3dRefs ) +{ +} + +void TokensFormulaContext::setTokens( const ApiTokenSequence& rTokens ) +{ + maTokens = rTokens; +} + +// ---------------------------------------------------------------------------- + +SimpleFormulaContext::SimpleFormulaContext( const Reference< XFormulaTokens >& rxTokens, + bool bRelativeAsOffset, bool bAlways3dRefs ) : + FormulaContext( bRelativeAsOffset, bAlways3dRefs ), + mxTokens( rxTokens ) +{ + OSL_ENSURE( mxTokens.is(), "SimpleFormulaContext::SimpleFormulaContext - missing XFormulaTokens interface" ); +} + +void SimpleFormulaContext::setTokens( const ApiTokenSequence& rTokens ) +{ + mxTokens->setTokens( rTokens ); +} + +// formula parser/formula compiler base class ================================= + +namespace { + +bool lclConvertToCellAddress( CellAddress& orAddress, const SingleReference& rSingleRef, sal_Int32 nExpectedSheet ) +{ + orAddress = CellAddress( static_cast< sal_Int16 >( rSingleRef.Sheet ), + rSingleRef.Column, rSingleRef.Row ); + return + ((nExpectedSheet < 0) || (nExpectedSheet == rSingleRef.Sheet)) && + !getFlag( rSingleRef.Flags, COLUMN_DELETED | ROW_DELETED | SHEET_DELETED ); +} + +bool lclConvertToCellRange( CellRangeAddress& orRange, const ComplexReference& rComplexRef, sal_Int32 nExpectedSheet ) +{ + orRange = CellRangeAddress( static_cast< sal_Int16 >( rComplexRef.Reference1.Sheet ), + rComplexRef.Reference1.Column, rComplexRef.Reference1.Row, + rComplexRef.Reference2.Column, rComplexRef.Reference2.Row ); + return + (rComplexRef.Reference1.Sheet == rComplexRef.Reference2.Sheet) && + ((nExpectedSheet < 0) || (nExpectedSheet == rComplexRef.Reference1.Sheet)) && + !getFlag( rComplexRef.Reference1.Flags, COLUMN_DELETED | ROW_DELETED | SHEET_DELETED ) && + !getFlag( rComplexRef.Reference2.Flags, COLUMN_DELETED | ROW_DELETED | SHEET_DELETED ); +} + +enum TokenToRangeListState { STATE_REF, STATE_SEP, STATE_OPEN, STATE_CLOSE, STATE_ERROR }; + +TokenToRangeListState lclProcessRef( ApiCellRangeList& orRanges, const Any& rData, sal_Int32 nExpectedSheet ) +{ + SingleReference aSingleRef; + if( rData >>= aSingleRef ) + { + CellAddress aAddress; + // ignore invalid addresses (with #REF! errors), but to not stop parsing + if( lclConvertToCellAddress( aAddress, aSingleRef, nExpectedSheet ) ) + orRanges.push_back( CellRangeAddress( aAddress.Sheet, aAddress.Column, aAddress.Row, aAddress.Column, aAddress.Row ) ); + return STATE_REF; + } + ComplexReference aComplexRef; + if( rData >>= aComplexRef ) + { + CellRangeAddress aRange; + // ignore invalid ranges (with #REF! errors), but to not stop parsing + if( lclConvertToCellRange( aRange, aComplexRef, nExpectedSheet ) ) + orRanges.push_back( aRange ); + return STATE_REF; + } + return STATE_ERROR; +} + +TokenToRangeListState lclProcessOpen( sal_Int32& ornParenLevel ) +{ + ++ornParenLevel; + return STATE_OPEN; +} + +TokenToRangeListState lclProcessClose( sal_Int32& ornParenLevel ) +{ + --ornParenLevel; + return (ornParenLevel >= 0) ? STATE_CLOSE : STATE_ERROR; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +FormulaProcessorBase::FormulaProcessorBase( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maFuncProv( rHelper ) +{ +} + +// ---------------------------------------------------------------------------- + +OUString FormulaProcessorBase::generateAddress2dString( const CellAddress& rAddress, bool bAbsolute ) +{ + return generateAddress2dString( BinAddress( rAddress ), bAbsolute ); +} + +OUString FormulaProcessorBase::generateAddress2dString( const BinAddress& rAddress, bool bAbsolute ) +{ + OUStringBuffer aBuffer; + // column + for( sal_Int32 nTemp = rAddress.mnCol; nTemp >= 0; (nTemp /= 26) -= 1 ) + aBuffer.insert( 0, sal_Unicode( 'A' + (nTemp % 26) ) ); + if( bAbsolute ) + aBuffer.insert( 0, sal_Unicode( '$' ) ); + // row + if( bAbsolute ) + aBuffer.append( sal_Unicode( '$' ) ); + aBuffer.append( static_cast< sal_Int32 >( rAddress.mnRow + 1 ) ); + return aBuffer.makeStringAndClear(); +} + +OUString FormulaProcessorBase::generateRange2dString( const CellRangeAddress& rRange, bool bAbsolute ) +{ + return generateRange2dString( BinRange( rRange ), bAbsolute ); +} + +OUString FormulaProcessorBase::generateRange2dString( const BinRange& rRange, bool bAbsolute ) +{ + OUStringBuffer aBuffer( generateAddress2dString( rRange.maFirst, bAbsolute ) ); + if( (rRange.getColCount() > 1) || (rRange.getRowCount() > 1) ) + aBuffer.append( sal_Unicode( ':' ) ).append( generateAddress2dString( rRange.maLast, bAbsolute ) ); + return aBuffer.makeStringAndClear(); +} + +OUString FormulaProcessorBase::generateRangeList2dString( const ApiCellRangeList& rRanges, + bool bAbsolute, sal_Unicode cSeparator, bool bEncloseMultiple ) +{ + OUStringBuffer aBuffer; + for( ApiCellRangeList::const_iterator aIt = rRanges.begin(), aEnd = rRanges.end(); aIt != aEnd; ++aIt ) + { + if( aBuffer.getLength() > 0 ) + aBuffer.append( cSeparator ); + aBuffer.append( generateRange2dString( *aIt, bAbsolute ) ); + } + if( bEncloseMultiple && (rRanges.size() > 1) ) + aBuffer.insert( 0, sal_Unicode( '(' ) ).append( sal_Unicode( ')' ) ); + return aBuffer.makeStringAndClear(); +} + +// ---------------------------------------------------------------------------- + +Any FormulaProcessorBase::extractReference( const ApiTokenSequence& rTokens ) const +{ + ApiTokenIterator aTokenIt( rTokens, maFuncProv.OPCODE_SPACES, true ); + if( aTokenIt.is() && (aTokenIt->OpCode == maFuncProv.OPCODE_PUSH) ) + { + Any aRefAny = aTokenIt->Data; + if( !(++aTokenIt).is() && (aRefAny.has< SingleReference >() || aRefAny.has< ComplexReference >()) ) + return aRefAny; + } + return Any(); +} + +void FormulaProcessorBase::extractCellRangeList( ApiCellRangeList& orRanges, + const ApiTokenSequence& rTokens, sal_Int32 nExpectedSheet ) const +{ + orRanges.clear(); + TokenToRangeListState eState = STATE_OPEN; + sal_Int32 nParenLevel = 0; + for( ApiTokenIterator aIt( rTokens, maFuncProv.OPCODE_SPACES, true ); aIt.is() && (eState != STATE_ERROR); ++aIt ) + { + sal_Int32 nOpCode = aIt->OpCode; + switch( eState ) + { + case STATE_REF: + if( nOpCode == maFuncProv.OPCODE_LIST ) eState = STATE_SEP; + else if( nOpCode == maFuncProv.OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel ); + else eState = STATE_ERROR; + break; + case STATE_SEP: + if( nOpCode == maFuncProv.OPCODE_PUSH ) eState = lclProcessRef( orRanges, aIt->Data, nExpectedSheet ); + else if( nOpCode == maFuncProv.OPCODE_LIST ) eState = STATE_SEP; + else if( nOpCode == maFuncProv.OPCODE_OPEN ) eState = lclProcessOpen( nParenLevel ); + else if( nOpCode == maFuncProv.OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel ); + else eState = STATE_ERROR; + break; + case STATE_OPEN: + if( nOpCode == maFuncProv.OPCODE_PUSH ) eState = lclProcessRef( orRanges, aIt->Data, nExpectedSheet ); + else if( nOpCode == maFuncProv.OPCODE_LIST ) eState = STATE_SEP; + else if( nOpCode == maFuncProv.OPCODE_OPEN ) eState = lclProcessOpen( nParenLevel ); + else if( nOpCode == maFuncProv.OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel ); + else eState = STATE_ERROR; + break; + case STATE_CLOSE: + if( nOpCode == maFuncProv.OPCODE_LIST ) eState = STATE_SEP; + else if( nOpCode == maFuncProv.OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel ); + else eState = STATE_ERROR; + break; + default:; + } + } + + if( eState == STATE_ERROR ) + orRanges.clear(); + else + getAddressConverter().validateCellRangeList( orRanges, false ); +} + +bool FormulaProcessorBase::extractString( OUString& orString, const ApiTokenSequence& rTokens ) const +{ + ApiTokenIterator aTokenIt( rTokens, maFuncProv.OPCODE_SPACES, true ); + return aTokenIt.is() && (aTokenIt->OpCode == maFuncProv.OPCODE_PUSH) && (aTokenIt->Data >>= orString) && !(++aTokenIt).is(); +} + +void FormulaProcessorBase::convertStringToStringList( + ApiTokenSequence& orTokens, sal_Unicode cStringSep, bool bTrimLeadingSpaces ) const +{ + OUString aString; + if( extractString( aString, orTokens ) && (aString.getLength() > 0) ) + { + ::std::vector< ApiToken > aNewTokens; + sal_Int32 nPos = 0; + sal_Int32 nLen = aString.getLength(); + while( (0 <= nPos) && (nPos < nLen) ) + { + OUString aEntry = aString.getToken( 0, cStringSep, nPos ); + if( bTrimLeadingSpaces ) + { + sal_Int32 nStart = 0; + while( (nStart < aEntry.getLength()) && (aEntry[ nStart ] == ' ') ) ++nStart; + aEntry = aEntry.copy( nStart ); + } + if( !aNewTokens.empty() ) + aNewTokens.push_back( ApiToken( maFuncProv.OPCODE_SEP, Any() ) ); + aNewTokens.push_back( ApiToken( maFuncProv.OPCODE_PUSH, Any( aEntry ) ) ); + } + orTokens = ContainerHelper::vectorToSequence( aNewTokens ); + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/formulaparser.cxx b/oox/source/xls/formulaparser.cxx new file mode 100644 index 000000000000..165d8f2b2873 --- /dev/null +++ b/oox/source/xls/formulaparser.cxx @@ -0,0 +1,2595 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: formulaparser.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/formulaparser.hxx" +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/FormulaToken.hpp> +#include <com/sun/star/sheet/ReferenceFlags.hpp> +#include <com/sun/star/sheet/SingleReference.hpp> +#include <com/sun/star/sheet/ComplexReference.hpp> +#include <com/sun/star/sheet/XFormulaParser.hpp> +#include "oox/helper/containerhelper.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/defnamesbuffer.hxx" +#include "oox/xls/externallinkbuffer.hxx" +#include "oox/xls/tablebuffer.hxx" +#include "oox/xls/worksheethelper.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::sheet::SingleReference; +using ::com::sun::star::sheet::ComplexReference; +using ::com::sun::star::sheet::XFormulaParser; +using namespace ::com::sun::star::sheet::ReferenceFlags; + +namespace oox { +namespace xls { + +// parser implementation base ================================================= + +class FormulaParserImpl : public WorkbookHelper +{ +public: + explicit FormulaParserImpl( + const WorkbookHelper& rHelper, + const FunctionProvider& rFuncProv ); + + /** Converts an XML formula string. */ + virtual void importOoxFormula( + FormulaContext& rContext, + const OUString& rFormulaString ); + + /** Imports and converts a OOBIN token array from the passed stream. */ + virtual void importOobFormula( + FormulaContext& rContext, + RecordInputStream& rStrm ); + + /** Imports and converts a BIFF token array from the passed stream. */ + virtual void importBiffFormula( + FormulaContext& rContext, + BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ); + + /** Finalizes the passed token array after import (e.g. adjusts function + parameters) and sets the formula using the passed context. */ + void setFormula( + FormulaContext& rContext, + const ApiTokenSequence& rTokens ); + +protected: + /** Sets the current formula context used for import. */ + inline FormulaContext& getFormulaContext() const { return *mpContext; } + + /** Sets the current formula context used for import. */ + void initializeImport( FormulaContext& rContext ); + /** Finalizes the passed token array after import. */ + void finalizeImport( const ApiTokenSequence& rTokens ); + /** Finalizes the internal token storage after import. */ + void finalizeImport(); + + /** Inserts a shared formula using the current formula context and passed base address. */ + void setSharedFormula( const BinAddress& rBaseAddr ); + + // token array ------------------------------------------------------------ + + bool resetSpaces(); + inline void incLeadingSpaces( sal_Int32 nSpaces ) { mnLeadingSpaces += nSpaces; } + inline void incOpeningSpaces( sal_Int32 nSpaces ) { mnOpeningSpaces += nSpaces; } + inline void incClosingSpaces( sal_Int32 nSpaces ) { mnClosingSpaces += nSpaces; } + + size_t getFormulaSize() const; + Any& appendRawToken( sal_Int32 nOpCode ); + Any& insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd ); + size_t appendSpacesToken( sal_Int32 nSpaces ); + size_t insertSpacesToken( sal_Int32 nSpaces, size_t nIndexFromEnd ); + + size_t getOperandSize( size_t nOpCountFromEnd, size_t nOpIndex ) const; + void pushOperandSize( size_t nSize ); + size_t popOperandSize(); + + ApiToken& getOperandToken( size_t nOpCountFromEnd, size_t nOpIndex, size_t nTokenIndex ); + void removeOperand( size_t nOpCountFromEnd, size_t nOpIndex ); + void removeLastOperands( size_t nOpCountFromEnd ); + + bool pushOperandToken( sal_Int32 nOpCode, sal_Int32 nSpaces ); + bool pushAnyOperandToken( const Any& rAny, sal_Int32 nOpCode, sal_Int32 nSpaces ); + template< typename Type > + bool pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, sal_Int32 nSpaces ); + template< typename Type > + inline bool pushValueOperandToken( const Type& rValue, sal_Int32 nSpaces ) + { return pushValueOperandToken( rValue, mrFuncProv.OPCODE_PUSH, nSpaces ); } + bool pushParenthesesOperandToken( sal_Int32 nOpeningSpaces, sal_Int32 nClosingSpaces ); + bool pushUnaryPreOperatorToken( sal_Int32 nOpCode, sal_Int32 nSpaces ); + bool pushUnaryPostOperatorToken( sal_Int32 nOpCode, sal_Int32 nSpaces ); + bool pushBinaryOperatorToken( sal_Int32 nOpCode, sal_Int32 nSpaces ); + bool pushParenthesesOperatorToken( sal_Int32 nOpeningSpaces, sal_Int32 nClosingSpaces ); + bool pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, sal_Int32 nLeadingSpaces, sal_Int32 nClosingSpaces ); + bool pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, sal_Int32 nLeadingSpaces, sal_Int32 nClosingSpaces ); + + bool pushOperand( sal_Int32 nOpCode ); + bool pushAnyOperand( const Any& rAny, sal_Int32 nOpCode ); + template< typename Type > + bool pushValueOperand( const Type& rValue, sal_Int32 nOpCode ); + template< typename Type > + inline bool pushValueOperand( const Type& rValue ) + { return pushValueOperand( rValue, mrFuncProv.OPCODE_PUSH ); } + bool pushBoolOperand( bool bValue ); + bool pushErrorOperand( double fEncodedError ); + bool pushBiffErrorOperand( sal_uInt8 nErrorCode ); + bool pushParenthesesOperand(); + bool pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushNlrOperand( const BinSingleRef2d& rRef ); + bool pushEmbeddedRefOperand( const DefinedNameBase& rName ); + bool pushDefinedNameOperand( const DefinedNameRef& rxDefName ); + bool pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem ); + bool pushExternalNameOperand( const ExternalNameRef& rxExtName, ExternalLinkType eLinkType ); + + bool pushUnaryPreOperator( sal_Int32 nOpCode ); + bool pushUnaryPostOperator( sal_Int32 nOpCode ); + bool pushBinaryOperator( sal_Int32 nOpCode ); + bool pushParenthesesOperator(); + bool pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount ); + bool pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount ); + +private: + // reference conversion --------------------------------------------------- + + void initReference2d( SingleReference& orApiRef ) const; + void initReference3d( SingleReference& orApiRef, sal_Int32 nSheet ) const; + void convertColRow( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bRelativeAsOffset ) const; + void convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const; + void convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const; + + // finalize token sequence ------------------------------------------------ + + typedef ::std::vector< const ApiToken* > ParameterPosVector; + + void processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd ); + const ApiToken* processParameters( const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd ); + + bool isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const; + OUString getExternCallParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const; + const ApiToken* skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const; + const ApiToken* findParameters( ParameterPosVector& rParams, const ApiToken* pToken, const ApiToken* pTokenEnd ) const; + void appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam ); + void appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount ); + + void appendFinalToken( const ApiToken& rToken ); + Any& appendFinalToken( sal_Int32 nOpCode ); + +protected: + const FunctionProvider& mrFuncProv; /// Function info provider. + const sal_Int32 mnMaxApiCol; /// Maximum column index in own document. + const sal_Int32 mnMaxApiRow; /// Maximum row index in own document. + const sal_Int32 mnMaxXlsCol; /// Maximum column index in imported document. + const sal_Int32 mnMaxXlsRow; /// Maximum row index in imported document. + +private: + typedef ::std::vector< ApiToken > ApiTokenVector; + typedef ::std::vector< size_t > SizeTypeVector; + + ApiTokenVector maTokenStorage; /// Raw unordered token storage. + SizeTypeVector maTokenIndexes; /// Indexes into maTokenStorage. + SizeTypeVector maOperandSizeStack; /// Stack with token sizes per operand. + FormulaContext* mpContext; /// Current formula context. + sal_Int32 mnLeadingSpaces; /// Current number of spaces before next token. + sal_Int32 mnOpeningSpaces; /// Current number of spaces before opening parenthesis. + sal_Int32 mnClosingSpaces; /// Current number of spaces before closing parenthesis. +}; + +// ---------------------------------------------------------------------------- + +FormulaParserImpl::FormulaParserImpl( const WorkbookHelper& rHelper, const FunctionProvider& rFuncProv ) : + WorkbookHelper( rHelper ), + mrFuncProv( rFuncProv ), + mnMaxApiCol( rHelper.getAddressConverter().getMaxApiAddress().Column ), + mnMaxApiRow( rHelper.getAddressConverter().getMaxApiAddress().Row ), + mnMaxXlsCol( rHelper.getAddressConverter().getMaxXlsAddress().Column ), + mnMaxXlsRow( rHelper.getAddressConverter().getMaxXlsAddress().Row ), + mpContext( 0 ) +{ + maTokenStorage.reserve( 0x2000 ); + maTokenIndexes.reserve( 0x2000 ); + maOperandSizeStack.reserve( 256 ); + resetSpaces(); +} + +void FormulaParserImpl::importOoxFormula( FormulaContext&, const OUString& ) +{ + OSL_ENSURE( false, "FormulaParserImpl::importOoxFormula - not implemented" ); +} + +void FormulaParserImpl::importOobFormula( FormulaContext&, RecordInputStream& ) +{ + OSL_ENSURE( false, "FormulaParserImpl::importOobFormula - not implemented" ); +} + +void FormulaParserImpl::importBiffFormula( FormulaContext&, BiffInputStream&, const sal_uInt16* ) +{ + OSL_ENSURE( false, "FormulaParserImpl::importBiffFormula - not implemented" ); +} + +void FormulaParserImpl::setFormula( FormulaContext& rContext, const ApiTokenSequence& rTokens ) +{ + initializeImport( rContext ); + finalizeImport( rTokens ); +} + +void FormulaParserImpl::initializeImport( FormulaContext& rContext ) +{ + maTokenStorage.clear(); + maTokenIndexes.clear(); + maOperandSizeStack.clear(); + mpContext = &rContext; +} + +void FormulaParserImpl::finalizeImport( const ApiTokenSequence& rTokens ) +{ + maTokenStorage.clear(); + const ApiToken* pToken = rTokens.getConstArray(); + processTokens( pToken, pToken + rTokens.getLength() ); + if( !maTokenStorage.empty() ) + mpContext->setTokens( ContainerHelper::vectorToSequence( maTokenStorage ) ); +} + +void FormulaParserImpl::finalizeImport() +{ + ApiTokenSequence aTokens( static_cast< sal_Int32 >( maTokenIndexes.size() ) ); + if( aTokens.hasElements() ) + { + ApiToken* pToken = aTokens.getArray(); + for( SizeTypeVector::const_iterator aIt = maTokenIndexes.begin(), aEnd = maTokenIndexes.end(); aIt != aEnd; ++aIt, ++pToken ) + *pToken = maTokenStorage[ *aIt ]; + } + finalizeImport( aTokens ); +} + +void FormulaParserImpl::setSharedFormula( const BinAddress& rBaseAddr ) +{ + CellAddress aApiBaseAddr; + if( getAddressConverter().convertToCellAddress( aApiBaseAddr, rBaseAddr, mpContext->getBaseAddress().Sheet, false ) ) + mpContext->setSharedFormula( aApiBaseAddr ); +} + +// token array ---------------------------------------------------------------- + +bool FormulaParserImpl::resetSpaces() +{ + mnLeadingSpaces = mnOpeningSpaces = mnClosingSpaces = 0; + return true; +} + +size_t FormulaParserImpl::getFormulaSize() const +{ + return maTokenIndexes.size(); +} + +Any& FormulaParserImpl::appendRawToken( sal_Int32 nOpCode ) +{ + size_t nTokenIndex = maTokenStorage.size(); + maTokenStorage.resize( nTokenIndex + 1 ); + maTokenStorage.back().OpCode = nOpCode; + maTokenIndexes.push_back( nTokenIndex ); + return maTokenStorage.back().Data; +} + +Any& FormulaParserImpl::insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd ) +{ + size_t nTokenIndex = maTokenStorage.size(); + maTokenStorage.resize( nTokenIndex + 1 ); + maTokenStorage.back().OpCode = nOpCode; + maTokenIndexes.insert( maTokenIndexes.end() - nIndexFromEnd, nTokenIndex ); + return maTokenStorage.back().Data; +} + +size_t FormulaParserImpl::appendSpacesToken( sal_Int32 nSpaces ) +{ + if( nSpaces > 0 ) + { + appendRawToken( mrFuncProv.OPCODE_SPACES ) <<= nSpaces; + return 1; + } + return 0; +} + +size_t FormulaParserImpl::insertSpacesToken( sal_Int32 nSpaces, size_t nIndexFromEnd ) +{ + if( nSpaces > 0 ) + { + insertRawToken( mrFuncProv.OPCODE_SPACES, nIndexFromEnd ) <<= nSpaces; + return 1; + } + return 0; +} + +size_t FormulaParserImpl::getOperandSize( size_t nOpCountFromEnd, size_t nOpIndex ) const +{ + OSL_ENSURE( (nOpIndex < nOpCountFromEnd) && (nOpCountFromEnd <= maOperandSizeStack.size()), + "FormulaParserImpl::getOperandSize - invalid parameters" ); + return maOperandSizeStack[ maOperandSizeStack.size() - nOpCountFromEnd + nOpIndex ]; +} + +void FormulaParserImpl::pushOperandSize( size_t nSize ) +{ + maOperandSizeStack.push_back( nSize ); +} + +size_t FormulaParserImpl::popOperandSize() +{ + OSL_ENSURE( !maOperandSizeStack.empty(), "FormulaParserImpl::popOperandSize - invalid call" ); + size_t nOpSize = maOperandSizeStack.back(); + maOperandSizeStack.pop_back(); + return nOpSize; +} + +ApiToken& FormulaParserImpl::getOperandToken( size_t nOpCountFromEnd, size_t nOpIndex, size_t nTokenIndex ) +{ + OSL_ENSURE( getOperandSize( nOpCountFromEnd, nOpIndex ) > nTokenIndex, + "FormulaParserImpl::getOperandToken - invalid parameters" ); + SizeTypeVector::const_iterator aIndexIt = maTokenIndexes.end(); + for( SizeTypeVector::const_iterator aEnd = maOperandSizeStack.end(), aIt = aEnd - nOpCountFromEnd + nOpIndex; aIt != aEnd; ++aIt ) + aIndexIt -= *aIt; + return maTokenStorage[ *(aIndexIt + nTokenIndex) ]; +} + +void FormulaParserImpl::removeOperand( size_t nOpCountFromEnd, size_t nOpIndex ) +{ + OSL_ENSURE( (nOpIndex < nOpCountFromEnd) && (nOpCountFromEnd <= maOperandSizeStack.size()), + "FormulaParserImpl::removeOperand - invalid parameters" ); + // remove indexes into token storage, but do not touch storage itself + SizeTypeVector::iterator aSizeEnd = maOperandSizeStack.end(); + SizeTypeVector::iterator aSizeIt = aSizeEnd - nOpCountFromEnd + nOpIndex; + size_t nRemainingSize = 0; + for( SizeTypeVector::iterator aIt = aSizeIt + 1; aIt != aSizeEnd; ++aIt ) + nRemainingSize += *aIt; + maTokenIndexes.erase( maTokenIndexes.end() - nRemainingSize - *aSizeIt, maTokenIndexes.end() - nRemainingSize ); + maOperandSizeStack.erase( aSizeIt ); +} + +void FormulaParserImpl::removeLastOperands( size_t nOpCountFromEnd ) +{ + for( size_t nOpIndex = 0; nOpIndex < nOpCountFromEnd; ++nOpIndex ) + removeOperand( 1, 0 ); +} + +bool FormulaParserImpl::pushOperandToken( sal_Int32 nOpCode, sal_Int32 nSpaces ) +{ + size_t nSpacesSize = appendSpacesToken( nSpaces ); + appendRawToken( nOpCode ); + pushOperandSize( nSpacesSize + 1 ); + return true; +} + +bool FormulaParserImpl::pushAnyOperandToken( const Any& rAny, sal_Int32 nOpCode, sal_Int32 nSpaces ) +{ + size_t nSpacesSize = appendSpacesToken( nSpaces ); + appendRawToken( nOpCode ) = rAny; + pushOperandSize( nSpacesSize + 1 ); + return true; +} + +template< typename Type > +bool FormulaParserImpl::pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, sal_Int32 nSpaces ) +{ + size_t nSpacesSize = appendSpacesToken( nSpaces ); + appendRawToken( nOpCode ) <<= rValue; + pushOperandSize( nSpacesSize + 1 ); + return true; +} + +bool FormulaParserImpl::pushParenthesesOperandToken( sal_Int32 nOpeningSpaces, sal_Int32 nClosingSpaces ) +{ + size_t nSpacesSize = appendSpacesToken( nOpeningSpaces ); + appendRawToken( mrFuncProv.OPCODE_OPEN ); + nSpacesSize += appendSpacesToken( nClosingSpaces ); + appendRawToken( mrFuncProv.OPCODE_CLOSE ); + pushOperandSize( nSpacesSize + 2 ); + return true; +} + +bool FormulaParserImpl::pushUnaryPreOperatorToken( sal_Int32 nOpCode, sal_Int32 nSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 1; + if( bOk ) + { + size_t nOpSize = popOperandSize(); + size_t nSpacesSize = insertSpacesToken( nSpaces, nOpSize ); + insertRawToken( nOpCode, nOpSize ); + pushOperandSize( nOpSize + nSpacesSize + 1 ); + } + return bOk; +} + +bool FormulaParserImpl::pushUnaryPostOperatorToken( sal_Int32 nOpCode, sal_Int32 nSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 1; + if( bOk ) + { + size_t nOpSize = popOperandSize(); + size_t nSpacesSize = appendSpacesToken( nSpaces ); + appendRawToken( nOpCode ); + pushOperandSize( nOpSize + nSpacesSize + 1 ); + } + return bOk; +} + +bool FormulaParserImpl::pushBinaryOperatorToken( sal_Int32 nOpCode, sal_Int32 nSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 2; + if( bOk ) + { + size_t nOp2Size = popOperandSize(); + size_t nOp1Size = popOperandSize(); + size_t nSpacesSize = insertSpacesToken( nSpaces, nOp2Size ); + insertRawToken( nOpCode, nOp2Size ); + pushOperandSize( nOp1Size + nSpacesSize + 1 + nOp2Size ); + } + return bOk; +} + +bool FormulaParserImpl::pushParenthesesOperatorToken( sal_Int32 nOpeningSpaces, sal_Int32 nClosingSpaces ) +{ + bool bOk = maOperandSizeStack.size() >= 1; + if( bOk ) + { + size_t nOpSize = popOperandSize(); + size_t nSpacesSize = insertSpacesToken( nOpeningSpaces, nOpSize ); + insertRawToken( mrFuncProv.OPCODE_OPEN, nOpSize ); + nSpacesSize += appendSpacesToken( nClosingSpaces ); + appendRawToken( mrFuncProv.OPCODE_CLOSE ); + pushOperandSize( nOpSize + nSpacesSize + 2 ); + } + return bOk; +} + +bool FormulaParserImpl::pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, sal_Int32 nLeadingSpaces, sal_Int32 nClosingSpaces ) +{ + /* #i70925# if there are not enough tokens available on token stack, do + not exit with error, but reduce parameter count. */ + nParamCount = ::std::min( maOperandSizeStack.size(), nParamCount ); + + // convert all parameters on stack to a single operand separated with OPCODE_SEP + bool bOk = true; + for( size_t nParam = 1; bOk && (nParam < nParamCount); ++nParam ) + bOk = pushBinaryOperatorToken( mrFuncProv.OPCODE_SEP, 0 ); + + // add function parentheses and function name + return bOk && + ((nParamCount > 0) ? pushParenthesesOperatorToken( 0, nClosingSpaces ) : pushParenthesesOperandToken( 0, nClosingSpaces )) && + pushUnaryPreOperatorToken( nOpCode, nLeadingSpaces ); +} + +bool FormulaParserImpl::pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, sal_Int32 nLeadingSpaces, sal_Int32 nClosingSpaces ) +{ + bool bOk = pushFunctionOperatorToken( rFuncInfo.mnApiOpCode, nParamCount, nLeadingSpaces, nClosingSpaces ); + // try to create an external add-in call for the passed built-in function + if( bOk && (rFuncInfo.maExtProgName.getLength() > 0) ) + getOperandToken( 1, 0, 0 ).Data <<= rFuncInfo.maExtProgName; + return bOk; +} + +bool FormulaParserImpl::pushOperand( sal_Int32 nOpCode ) +{ + return pushOperandToken( nOpCode, mnLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushAnyOperand( const Any& rAny, sal_Int32 nOpCode ) +{ + return pushAnyOperandToken( rAny, nOpCode, mnLeadingSpaces ) && resetSpaces(); +} + +template< typename Type > +bool FormulaParserImpl::pushValueOperand( const Type& rValue, sal_Int32 nOpCode ) +{ + return pushValueOperandToken( rValue, nOpCode, mnLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushBoolOperand( bool bValue ) +{ + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromOobFuncId( bValue ? OOBIN_FUNC_TRUE : OOBIN_FUNC_FALSE ) ) + return pushFunctionOperator( pFuncInfo->mnApiOpCode, 0 ); + return pushValueOperand< double >( bValue ? 1.0 : 0.0 ); +} + +bool FormulaParserImpl::pushErrorOperand( double fEncodedError ) +{ + // HACK: enclose all error codes into an 1x1 matrix + // start token array with opening brace and leading spaces + pushOperand( mrFuncProv.OPCODE_ARRAY_OPEN ); + size_t nOpSize = popOperandSize(); + size_t nOldArraySize = maTokenIndexes.size(); + // push a double containing the Calc error code + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= fEncodedError; + // close token array and set resulting operand size + appendRawToken( mrFuncProv.OPCODE_ARRAY_CLOSE ); + pushOperandSize( nOpSize + maTokenIndexes.size() - nOldArraySize ); + return true; +} + +bool FormulaParserImpl::pushBiffErrorOperand( sal_uInt8 nErrorCode ) +{ + return pushErrorOperand( BiffHelper::calcDoubleFromError( nErrorCode ) ); +} + +bool FormulaParserImpl::pushParenthesesOperand() +{ + return pushParenthesesOperandToken( mnOpeningSpaces, mnClosingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + SingleReference aApiRef; + convertReference2d( aApiRef, rRef, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); +} + +bool FormulaParserImpl::pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + ComplexReference aApiRef; + convertReference2d( aApiRef, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); +} + +bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + if( rSheetRange.is3dRange() ) + { + // single-cell-range over several sheets, needs to create a ComplexReference + ComplexReference aApiRef; + convertReference3d( aApiRef, rSheetRange, rRef, rRef, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); + } + SingleReference aApiRef; + convertReference3d( aApiRef, rSheetRange.isDeleted() ? -1 : rSheetRange.mnFirst, rRef, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); +} + +bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + ComplexReference aApiRef; + convertReference3d( aApiRef, rSheetRange, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset ); + return pushValueOperand( aApiRef ); +} + +bool FormulaParserImpl::pushNlrOperand( const BinSingleRef2d& rRef ) +{ + SingleReference aApiRef; + convertReference2d( aApiRef, rRef, false, false ); + return pushValueOperand( aApiRef, mrFuncProv.OPCODE_NLR ); +} + +bool FormulaParserImpl::pushEmbeddedRefOperand( const DefinedNameBase& rName ) +{ + Any aRefAny = rName.getReference( mpContext->getBaseAddress() ); + return aRefAny.hasValue() ? pushAnyOperand( aRefAny, mrFuncProv.OPCODE_PUSH ) : pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool FormulaParserImpl::pushDefinedNameOperand( const DefinedNameRef& rxDefName ) +{ + if( !rxDefName ) + return pushBiffErrorOperand( BIFF_ERR_NAME ); + if( rxDefName->isMacroFunc( false ) ) + return pushValueOperand( rxDefName->getOoxName(), mrFuncProv.OPCODE_MACRO ); + if( rxDefName->getTokenIndex() >= 0 ) + return pushValueOperand( rxDefName->getTokenIndex(), mrFuncProv.OPCODE_NAME ); + return pushEmbeddedRefOperand( *rxDefName ); +} + +bool FormulaParserImpl::pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem ) +{ + // create the function call DDE("server";"topic";"item") + return + pushValueOperandToken( rDdeServer, 0 ) && + pushValueOperandToken( rDdeTopic, 0 ) && + pushValueOperandToken( rDdeItem, 0 ) && + pushFunctionOperator( mrFuncProv.OPCODE_DDE, 3 ); +} + +bool FormulaParserImpl::pushExternalNameOperand( const ExternalNameRef& rxExtName, ExternalLinkType eLinkType ) +{ + if( rxExtName.get() ) switch( eLinkType ) + { + case LINKTYPE_INTERNAL: + case LINKTYPE_EXTERNAL: + return pushEmbeddedRefOperand( *rxExtName ); + + case LINKTYPE_ANALYSIS: + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromOoxFuncName( rxExtName->getOoxName() ) ) + if( pFuncInfo->maExternCallName.getLength() > 0 ) + return pushValueOperand( pFuncInfo->maExternCallName, mrFuncProv.OPCODE_MACRO ); + break; + + case LINKTYPE_DDE: + { + OUString aDdeServer, aDdeTopic, aDdeItem; + if( rxExtName->getDdeLinkData( aDdeServer, aDdeTopic, aDdeItem ) ) + return pushDdeLinkOperand( aDdeServer, aDdeTopic, aDdeItem ); + } + break; + + default: + OSL_ENSURE( eLinkType != LINKTYPE_SELF, "FormulaParserImpl::pushExternalNameOperand - invalid call" ); + } + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool FormulaParserImpl::pushUnaryPreOperator( sal_Int32 nOpCode ) +{ + return pushUnaryPreOperatorToken( nOpCode, mnLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushUnaryPostOperator( sal_Int32 nOpCode ) +{ + return pushUnaryPostOperatorToken( nOpCode, mnLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushBinaryOperator( sal_Int32 nOpCode ) +{ + return pushBinaryOperatorToken( nOpCode, mnLeadingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushParenthesesOperator() +{ + return pushParenthesesOperatorToken( mnOpeningSpaces, mnClosingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount ) +{ + return pushFunctionOperatorToken( nOpCode, nParamCount, mnLeadingSpaces, mnClosingSpaces ) && resetSpaces(); +} + +bool FormulaParserImpl::pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount ) +{ + return pushFunctionOperatorToken( rFuncInfo, nParamCount, mnLeadingSpaces, mnClosingSpaces ) && resetSpaces(); +} + +// reference conversion ------------------------------------------------------- + +void FormulaParserImpl::initReference2d( SingleReference& orApiRef ) const +{ + if( mpContext->isAlways3dRefs() ) + { + initReference3d( orApiRef, mpContext->getBaseAddress().Sheet ); + } + else + { + orApiRef.Flags = SHEET_RELATIVE; + // #i10184# absolute sheet index needed for relative references in shared formulas + orApiRef.Sheet = mpContext->getBaseAddress().Sheet; + orApiRef.RelativeSheet = 0; + } +} + +void FormulaParserImpl::initReference3d( SingleReference& orApiRef, sal_Int32 nSheet ) const +{ + orApiRef.Flags = SHEET_3D; + if( nSheet < 0 ) + { + orApiRef.Sheet = 0; + orApiRef.Flags |= SHEET_DELETED; + } + else + { + orApiRef.Sheet = nSheet; + } +} + +void FormulaParserImpl::convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const +{ + if( bDeleted ) + { + orApiRef.Column = 0; + orApiRef.Row = 0; + // no explicit information about whether row or column is deleted + orApiRef.Flags |= COLUMN_DELETED | ROW_DELETED; + } + else + { + // column/row indexes and flags + setFlag( orApiRef.Flags, COLUMN_RELATIVE, rRef.mbColRel ); + setFlag( orApiRef.Flags, ROW_RELATIVE, rRef.mbRowRel ); + (rRef.mbColRel ? orApiRef.RelativeColumn : orApiRef.Column) = rRef.mnCol; + (rRef.mbRowRel ? orApiRef.RelativeRow : orApiRef.Row) = rRef.mnRow; + // convert absolute indexes to relative offsets used in API + if( !bRelativeAsOffset ) + { + if( rRef.mbColRel ) + orApiRef.RelativeColumn -= mpContext->getBaseAddress().Column; + if( rRef.mbRowRel ) + orApiRef.RelativeRow -= mpContext->getBaseAddress().Row; + } + } +} + +void FormulaParserImpl::convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const +{ + convertReference( orApiRef.Reference1, rRef1, bDeleted, bRelativeAsOffset ); + convertReference( orApiRef.Reference2, rRef2, bDeleted, bRelativeAsOffset ); + /* Handle references to complete rows or columns (e.g. $1:$2 or C:D), + need to expand or shrink to limits of own document. */ + if( !bDeleted && !rRef1.mbColRel && !rRef2.mbColRel && (orApiRef.Reference1.Column == 0) && (orApiRef.Reference2.Column == mnMaxXlsCol) ) + orApiRef.Reference2.Column = mnMaxApiCol; + if( !bDeleted && !rRef1.mbRowRel && !rRef2.mbRowRel && (orApiRef.Reference1.Row == 0) && (orApiRef.Reference2.Row == mnMaxXlsRow) ) + orApiRef.Reference2.Row = mnMaxApiRow; +} + +void FormulaParserImpl::convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference2d( orApiRef ); + convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset ); +} + +void FormulaParserImpl::convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference2d( orApiRef.Reference1 ); + initReference2d( orApiRef.Reference2 ); + convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset ); + // remove sheet name from second part of reference + setFlag( orApiRef.Reference2.Flags, SHEET_3D, false ); +} + +void FormulaParserImpl::convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference3d( orApiRef, nSheet ); + convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset ); +} + +void FormulaParserImpl::convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const +{ + initReference3d( orApiRef.Reference1, rSheetRange.isDeleted() ? -1 : rSheetRange.mnFirst ); + initReference3d( orApiRef.Reference2, rSheetRange.isDeleted() ? -1 : rSheetRange.mnLast ); + convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset ); + // remove sheet name from second part of reference + setFlag( orApiRef.Reference2.Flags, SHEET_3D, rSheetRange.is3dRange() ); +} + +// finalize token sequence ---------------------------------------------------- + +void FormulaParserImpl::processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd ) +{ + while( pToken < pTokenEnd ) + { + // push the current token into the vector + appendFinalToken( *pToken ); + // try to process a function, otherwise go to next token + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromApiToken( *pToken ) ) + pToken = processParameters( *pFuncInfo, pToken + 1, pTokenEnd ); + else + ++pToken; + } +} + +const ApiToken* FormulaParserImpl::processParameters( + const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd ) +{ + // remember position of the token containing the function op-code + size_t nFuncNameIdx = maTokenStorage.size() - 1; + + // process a function, if an OPCODE_OPEN token is following + OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_OPEN), "FormulaParserImpl::processParameters - OPCODE_OPEN expected" ); + if( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_OPEN) ) + { + // append the OPCODE_OPEN token to the vector + appendFinalToken( mrFuncProv.OPCODE_OPEN ); + + // store positions of OPCODE_OPEN, parameter separators, and OPCODE_CLOSE + ParameterPosVector aParams; + pToken = findParameters( aParams, pToken, pTokenEnd ); + OSL_ENSURE( aParams.size() >= 2, "FormulaParserImpl::processParameters - missing tokens" ); + size_t nParamCount = aParams.size() - 1; + + if( (nParamCount == 1) && isEmptyParameter( aParams[ 0 ] + 1, aParams[ 1 ] ) ) + { + /* Empty pair of parentheses -> function call without parameters, + process parameter, there might be spaces between parentheses. */ + processTokens( aParams[ 0 ] + 1, aParams[ 1 ] ); + } + else + { + const FunctionInfo* pRealFuncInfo = &rFuncInfo; + ParameterPosVector::const_iterator aPosIt = aParams.begin(); + + // preprocess add-ins, first parameter is reference to function name + if( rFuncInfo.mnOobFuncId == OOBIN_FUNC_EXTERNCALL ) + { + OUString aName = getExternCallParameter( *aPosIt + 1, *(aPosIt + 1) ); + if( const FunctionInfo* pExtFuncInfo = mrFuncProv.getFuncInfoFromExternCallName( aName ) ) + { + maTokenStorage[ nFuncNameIdx ].OpCode = pExtFuncInfo->mnApiOpCode; + // insert programmatic add-in function name + if( pExtFuncInfo->mnApiOpCode == mrFuncProv.OPCODE_EXTERNAL ) + maTokenStorage[ nFuncNameIdx ].Data <<= pExtFuncInfo->maExtProgName; + // prepare for following parameters + pRealFuncInfo = pExtFuncInfo; + --nParamCount; + ++aPosIt; + } + } + + // process all parameters + FuncInfoParamClassIterator aClassIt( *pRealFuncInfo ); + size_t nLastValidSize = maTokenStorage.size(); + size_t nLastValidCount = 0; + for( size_t nParam = 0; nParam < nParamCount; ++nParam, ++aPosIt, ++aClassIt ) + { + // add embedded Calc-only parameters + if( aClassIt.isCalcOnlyParam() ) + { + appendCalcOnlyParameter( *pRealFuncInfo, nParam ); + while( aClassIt.isCalcOnlyParam() ) ++aClassIt; + } + + const ApiToken* pParamBegin = *aPosIt + 1; + const ApiToken* pParamEnd = *(aPosIt + 1); + bool bIsEmpty = isEmptyParameter( pParamBegin, pParamEnd ); + + if( !aClassIt.isExcelOnlyParam() ) + { + // replace empty second and third parameter in IF function with zeros + if( (pRealFuncInfo->mnOobFuncId == OOBIN_FUNC_IF) && ((nParam == 1) || (nParam == 2)) && bIsEmpty ) + { + appendFinalToken( mrFuncProv.OPCODE_PUSH ) <<= static_cast< double >( 0.0 ); + bIsEmpty = false; + } + else + { + // process all tokens of the parameter + processTokens( pParamBegin, pParamEnd ); + } + // append parameter separator token + appendFinalToken( mrFuncProv.OPCODE_SEP ); + } + + /* #84453# Update size of new token sequence with valid parameters + to be able to remove trailing optional empty parameters. */ + if( !bIsEmpty || (nParam < pRealFuncInfo->mnMinParamCount) ) + { + nLastValidSize = maTokenStorage.size(); + nLastValidCount = nParam + 1; + } + } + + // #84453# remove trailing optional empty parameters + maTokenStorage.resize( nLastValidSize ); + + // add trailing Calc-only parameters + if( aClassIt.isCalcOnlyParam() ) + appendCalcOnlyParameter( *pRealFuncInfo, nLastValidCount ); + + // add optional parameters that are required in Calc + appendRequiredParameters( *pRealFuncInfo, nLastValidCount ); + + // remove last parameter separator token + if( maTokenStorage.back().OpCode == mrFuncProv.OPCODE_SEP ) + maTokenStorage.pop_back(); + } + + /* Append the OPCODE_CLOSE token to the vector, but only if there is + no OPCODE_BAD token at the end, this token already contains the + trailing closing parentheses. */ + if( (pTokenEnd - 1)->OpCode != mrFuncProv.OPCODE_BAD ) + appendFinalToken( mrFuncProv.OPCODE_CLOSE ); + } + + /* Replace OPCODE_EXTERNAL with OPCODE_NONAME to get #NAME! error in cell, + if no matching add-in function was found. */ + ApiToken& rFuncNameToken = maTokenStorage[ nFuncNameIdx ]; + if( (rFuncNameToken.OpCode == mrFuncProv.OPCODE_EXTERNAL) && !rFuncNameToken.Data.hasValue() ) + rFuncNameToken.OpCode = mrFuncProv.OPCODE_NONAME; + + return pToken; +} + +bool FormulaParserImpl::isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + while( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_SPACES) ) ++pToken; + if( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_MISSING) ) ++pToken; + while( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_SPACES) ) ++pToken; + return pToken == pTokenEnd; +} + +OUString FormulaParserImpl::getExternCallParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + OUString aExtCallName; + while( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_SPACES) ) ++pToken; + if( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_MACRO) ) (pToken++)->Data >>= aExtCallName; + while( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_SPACES) ) ++pToken; + return (pToken == pTokenEnd) ? aExtCallName : OUString(); +} + +const ApiToken* FormulaParserImpl::skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + // skip tokens between OPCODE_OPEN and OPCODE_CLOSE + OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_OPEN), "skipParentheses - OPCODE_OPEN expected" ); + ++pToken; + while( (pToken < pTokenEnd) && (pToken->OpCode != mrFuncProv.OPCODE_CLOSE) ) + { + if( pToken->OpCode == mrFuncProv.OPCODE_OPEN ) + pToken = skipParentheses( pToken, pTokenEnd ); + else + ++pToken; + } + // skip the OPCODE_CLOSE token + OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == mrFuncProv.OPCODE_BAD), "skipParentheses - OPCODE_CLOSE expected" ); + return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd; +} + +const ApiToken* FormulaParserImpl::findParameters( ParameterPosVector& rParams, + const ApiToken* pToken, const ApiToken* pTokenEnd ) const +{ + // push position of OPCODE_OPEN + OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_OPEN), "FormulaParserImpl::findParameters - OPCODE_OPEN expected" ); + rParams.push_back( pToken++ ); + + // find positions of parameter separators + while( (pToken < pTokenEnd) && (pToken->OpCode != mrFuncProv.OPCODE_CLOSE) ) + { + if( pToken->OpCode == mrFuncProv.OPCODE_OPEN ) + pToken = skipParentheses( pToken, pTokenEnd ); + else if( pToken->OpCode == mrFuncProv.OPCODE_SEP ) + rParams.push_back( pToken++ ); + else + ++pToken; + } + + // push position of OPCODE_CLOSE + OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == mrFuncProv.OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == mrFuncProv.OPCODE_BAD), "FormulaParserImpl::findParameters - OPCODE_CLOSE expected" ); + rParams.push_back( pToken ); + return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd; +} + +void FormulaParserImpl::appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam ) +{ + (void)nParam; // prevent 'unused' warning + switch( rFuncInfo.mnOobFuncId ) + { + case OOBIN_FUNC_FLOOR: + case OOBIN_FUNC_CEILING: + OSL_ENSURE( nParam == 2, "FormulaParserImpl::appendCalcOnlyParameter - unexpected parameter index" ); + appendFinalToken( mrFuncProv.OPCODE_PUSH ) <<= static_cast< double >( 1.0 ); + appendFinalToken( mrFuncProv.OPCODE_SEP ); + break; + } +} + +void FormulaParserImpl::appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount ) +{ + switch( rFuncInfo.mnOobFuncId ) + { + case OOBIN_FUNC_WEEKNUM: + if( nParamCount == 1 ) + { + appendFinalToken( mrFuncProv.OPCODE_PUSH ) <<= static_cast< double >( 1.0 ); + appendFinalToken( mrFuncProv.OPCODE_SEP ); + } + break; + } +} + +void FormulaParserImpl::appendFinalToken( const ApiToken& rToken ) +{ + if( (rToken.OpCode == mrFuncProv.OPCODE_MACRO) && !rToken.Data.hasValue() ) + { + appendFinalToken( mrFuncProv.OPCODE_ARRAY_OPEN ); + appendFinalToken( mrFuncProv.OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NAME ); + appendFinalToken( mrFuncProv.OPCODE_ARRAY_CLOSE ); + } + else + maTokenStorage.push_back( rToken ); +} + +Any& FormulaParserImpl::appendFinalToken( sal_Int32 nOpCode ) +{ + maTokenStorage.resize( maTokenStorage.size() + 1 ); + maTokenStorage.back().OpCode = nOpCode; + return maTokenStorage.back().Data; +} + +// OOX parser implementation ================================================== + +class OoxFormulaParserImpl : public FormulaParserImpl +{ +public: + explicit OoxFormulaParserImpl( + const WorkbookHelper& rHelper, + const FunctionProvider& rFuncProv ); + + virtual void importOoxFormula( + FormulaContext& rContext, + const OUString& rFormulaString ); + + virtual void importOobFormula( + FormulaContext& rContext, + RecordInputStream& rStrm ); + +private: + // import token contents and create API formula token --------------------- + + bool importAttrToken( RecordInputStream& rStrm ); + bool importSpaceToken( RecordInputStream& rStrm ); + bool importTableToken( RecordInputStream& rStrm ); + bool importArrayToken( RecordInputStream& rStrm ); + bool importRefToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importAreaToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRef3dToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importArea3dToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importMemAreaToken( RecordInputStream& rStrm, bool bAddData ); + bool importMemFuncToken( RecordInputStream& rStrm ); + bool importNameToken( RecordInputStream& rStrm ); + bool importNameXToken( RecordInputStream& rStrm ); + bool importFuncToken( RecordInputStream& rStrm ); + bool importFuncVarToken( RecordInputStream& rStrm ); + bool importExpToken( RecordInputStream& rStrm ); + + LinkSheetRange readSheetRange( RecordInputStream& rStrm ); + + void swapStreamPosition( RecordInputStream& rStrm ); + void skipMemAreaAddData( RecordInputStream& rStrm ); + + // convert BIN token and push API operand or operator --------------------- + + bool pushOobName( sal_Int32 nNameId ); + bool pushOobExtName( sal_Int32 nRefId, sal_Int32 nNameId ); + bool pushOobFunction( sal_uInt16 nFuncId ); + bool pushOobFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ); + +private: + Reference< XFormulaParser > mxParser; + PropertySet maParserProps; + const OUString maRefPosProp; + sal_Int32 mnAddDataPos; /// Current stream position for additional data (tExp, tArray, tMemArea). +}; + +// ---------------------------------------------------------------------------- + +OoxFormulaParserImpl::OoxFormulaParserImpl( const WorkbookHelper& rHelper, const FunctionProvider& rFuncProv ) : + FormulaParserImpl( rHelper, rFuncProv ), + maRefPosProp( CREATE_OUSTRING( "ReferencePosition" ) ), + mnAddDataPos( 0 ) +{ + try + { + Reference< XMultiServiceFactory > xFactory( getDocument(), UNO_QUERY_THROW ); + mxParser.set( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.sheet.FormulaParser" ) ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + } + OSL_ENSURE( mxParser.is(), "OoxFormulaParserImpl::OoxFormulaParserImpl - cannot create formula parser" ); + maParserProps.set( mxParser ); + maParserProps.setProperty( CREATE_OUSTRING( "CompileEnglish" ), true ); + maParserProps.setProperty( CREATE_OUSTRING( "R1C1Notation" ), false ); + maParserProps.setProperty( CREATE_OUSTRING( "Compatibility3DNotation" ), true ); + maParserProps.setProperty( CREATE_OUSTRING( "IgnoreLeadingSpaces" ), false ); + maParserProps.setProperty( CREATE_OUSTRING( "OpCodeMap" ), mrFuncProv.getOoxParserMap() ); +} + +void OoxFormulaParserImpl::importOoxFormula( + FormulaContext& rContext, const OUString& rFormulaString ) +{ + if( mxParser.is() ) + { + initializeImport( rContext ); + maParserProps.setProperty( maRefPosProp, getFormulaContext().getBaseAddress() ); + finalizeImport( mxParser->parseFormula( rFormulaString ) ); + } +} + +void OoxFormulaParserImpl::importOobFormula( FormulaContext& rContext, RecordInputStream& rStrm ) +{ + initializeImport( rContext ); + + sal_Int32 nFmlaSize = rStrm.readInt32(); + sal_Int32 nFmlaPos = rStrm.getRecPos(); + sal_Int32 nFmlaEndPos = nFmlaPos + nFmlaSize; + + rStrm.seek( nFmlaEndPos ); + sal_Int32 nAddDataSize = rStrm.readInt32(); + mnAddDataPos = rStrm.getRecPos(); + sal_Int32 nAddDataEndPos = mnAddDataPos + nAddDataSize; + rStrm.seek( nFmlaPos ); + + bool bOk = (nFmlaSize >= 0) && (nAddDataSize >= 0); + bool bRelativeAsOffset = getFormulaContext().isRelativeAsOffset(); + + while( bOk && rStrm.isValid() && (rStrm.getRecPos() < nFmlaEndPos) ) + { + sal_uInt8 nTokenId; + rStrm >> nTokenId; + sal_uInt8 nTokenClass = nTokenId & BIFF_TOKCLASS_MASK; + sal_uInt8 nBaseId = nTokenId & BIFF_TOKID_MASK; + + if( nTokenClass == BIFF_TOKCLASS_NONE ) + { + // base tokens + switch( nBaseId ) + { + case BIFF_TOKID_EXP: bOk = importExpToken( rStrm ); break; + case BIFF_TOKID_ADD: bOk = pushBinaryOperator( mrFuncProv.OPCODE_ADD ); break; + case BIFF_TOKID_SUB: bOk = pushBinaryOperator( mrFuncProv.OPCODE_SUB ); break; + case BIFF_TOKID_MUL: bOk = pushBinaryOperator( mrFuncProv.OPCODE_MULT ); break; + case BIFF_TOKID_DIV: bOk = pushBinaryOperator( mrFuncProv.OPCODE_DIV ); break; + case BIFF_TOKID_POWER: bOk = pushBinaryOperator( mrFuncProv.OPCODE_POWER ); break; + case BIFF_TOKID_CONCAT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_CONCAT ); break; + case BIFF_TOKID_LT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_LESS ); break; + case BIFF_TOKID_LE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_LESS_EQUAL ); break; + case BIFF_TOKID_EQ: bOk = pushBinaryOperator( mrFuncProv.OPCODE_EQUAL ); break; + case BIFF_TOKID_GE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_GREATER_EQUAL ); break; + case BIFF_TOKID_GT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_GREATER ); break; + case BIFF_TOKID_NE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_NOT_EQUAL ); break; + case BIFF_TOKID_ISECT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_INTERSECT ); break; + case BIFF_TOKID_LIST: bOk = pushBinaryOperator( mrFuncProv.OPCODE_LIST ); break; + case BIFF_TOKID_RANGE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_RANGE ); break; + case BIFF_TOKID_UPLUS: bOk = pushUnaryPreOperator( mrFuncProv.OPCODE_PLUS_SIGN ); break; + case BIFF_TOKID_UMINUS: bOk = pushUnaryPreOperator( mrFuncProv.OPCODE_MINUS_SIGN ); break; + case BIFF_TOKID_PERCENT: bOk = pushUnaryPostOperator( mrFuncProv.OPCODE_PERCENT ); break; + case BIFF_TOKID_PAREN: bOk = pushParenthesesOperator(); break; + case BIFF_TOKID_MISSARG: bOk = pushOperand( mrFuncProv.OPCODE_MISSING ); break; + case BIFF_TOKID_STR: bOk = pushValueOperand( rStrm.readString( false ) ); break; + case BIFF_TOKID_NLR: bOk = importTableToken( rStrm ); break; + case BIFF_TOKID_ATTR: bOk = importAttrToken( rStrm ); break; + case BIFF_TOKID_ERR: bOk = pushBiffErrorOperand( rStrm.readuInt8() ); break; + case BIFF_TOKID_BOOL: bOk = pushBoolOperand( rStrm.readuInt8() != BIFF_TOK_BOOL_FALSE ); break; + case BIFF_TOKID_INT: bOk = pushValueOperand< double >( rStrm.readuInt16() ); break; + case BIFF_TOKID_NUM: bOk = pushValueOperand( rStrm.readDouble() ); break; + default: bOk = false; + } + } + else + { + // classified tokens + switch( nBaseId ) + { + case BIFF_TOKID_ARRAY: bOk = importArrayToken( rStrm ); break; + case BIFF_TOKID_FUNC: bOk = importFuncToken( rStrm ); break; + case BIFF_TOKID_FUNCVAR: bOk = importFuncVarToken( rStrm ); break; + case BIFF_TOKID_NAME: bOk = importNameToken( rStrm ); break; + case BIFF_TOKID_REF: bOk = importRefToken( rStrm, false, false ); break; + case BIFF_TOKID_AREA: bOk = importAreaToken( rStrm, false, false ); break; + case BIFF_TOKID_MEMAREA: bOk = importMemAreaToken( rStrm, true ); break; + case BIFF_TOKID_MEMERR: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMNOMEM: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMFUNC: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_REFERR: bOk = importRefToken( rStrm, true, false ); break; + case BIFF_TOKID_AREAERR: bOk = importAreaToken( rStrm, true, false ); break; + case BIFF_TOKID_REFN: bOk = importRefToken( rStrm, false, true ); break; + case BIFF_TOKID_AREAN: bOk = importAreaToken( rStrm, false, true ); break; + case BIFF_TOKID_MEMAREAN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_MEMNOMEMN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_NAMEX: bOk = importNameXToken( rStrm ); break; + case BIFF_TOKID_REF3D: bOk = importRef3dToken( rStrm, false, bRelativeAsOffset ); break; + case BIFF_TOKID_AREA3D: bOk = importArea3dToken( rStrm, false, bRelativeAsOffset ); break; + case BIFF_TOKID_REFERR3D: bOk = importRef3dToken( rStrm, true, bRelativeAsOffset ); break; + case BIFF_TOKID_AREAERR3D: bOk = importArea3dToken( rStrm, true, bRelativeAsOffset ); break; + default: bOk = false; + } + } + } + + // build and finalize the token sequence + if( bOk && (rStrm.getRecPos() == nFmlaEndPos) && (mnAddDataPos == nAddDataEndPos) ) + finalizeImport(); + + // seek behind token array + if( (nFmlaSize >= 0) && (nAddDataSize >= 0) ) + rStrm.seek( nAddDataEndPos ); +} + +// import token contents and create API formula token ------------------------- + +bool OoxFormulaParserImpl::importAttrToken( RecordInputStream& rStrm ) +{ + bool bOk = true; + sal_uInt8 nType; + rStrm >> nType; + // equal flags in BIFF and OOBIN + switch( nType ) + { + case OOBIN_TOK_ATTR_VOLATILE: + case OOBIN_TOK_ATTR_IF: + case OOBIN_TOK_ATTR_SKIP: + case OOBIN_TOK_ATTR_ASSIGN: + case OOBIN_TOK_ATTR_IFERROR: + rStrm.skip( 2 ); + break; + case OOBIN_TOK_ATTR_CHOOSE: + rStrm.skip( 2 * rStrm.readuInt16() + 2 ); + break; + case OOBIN_TOK_ATTR_SUM: + rStrm.skip( 2 ); + bOk = pushOobFunction( OOBIN_FUNC_SUM, 1 ); + break; + case OOBIN_TOK_ATTR_SPACE: + case OOBIN_TOK_ATTR_SPACE_VOLATILE: + bOk = importSpaceToken( rStrm ); + break; + default: + bOk = false; + } + return bOk; +} + +bool OoxFormulaParserImpl::importSpaceToken( RecordInputStream& rStrm ) +{ + // equal constants in BIFF and OOX + sal_uInt8 nType, nCount; + rStrm >> nType >> nCount; + switch( nType ) + { + case BIFF_TOK_ATTR_SPACE_SP: + case BIFF_TOK_ATTR_SPACE_BR: + incLeadingSpaces( nCount ); + break; + case BIFF_TOK_ATTR_SPACE_SP_OPEN: + case BIFF_TOK_ATTR_SPACE_BR_OPEN: + incOpeningSpaces( nCount ); + break; + case BIFF_TOK_ATTR_SPACE_SP_CLOSE: + case BIFF_TOK_ATTR_SPACE_BR_CLOSE: + incClosingSpaces( nCount ); + break; + } + return true; +} + +bool OoxFormulaParserImpl::importTableToken( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags, nTableId, nCol1, nCol2; + rStrm.skip( 3 ); + rStrm >> nFlags >> nTableId; + rStrm.skip( 2 ); + rStrm >> nCol1 >> nCol2; + TableRef xTable = getTables().getTable( nTableId ); + sal_Int32 nTokenIndex = xTable.get() ? xTable->getTokenIndex() : -1; + if( nTokenIndex >= 0 ) + { + sal_Int32 nWidth = xTable->getWidth(); + sal_Int32 nHeight = xTable->getHeight(); + sal_Int32 nStartCol = 0; + sal_Int32 nEndCol = nWidth - 1; + sal_Int32 nStartRow = 0; + sal_Int32 nEndRow = nHeight - 1; + bool bFixedStartRow = true; + bool bFixedHeight = false; + + bool bSingleCol = getFlag( nFlags, OOBIN_TOK_TABLE_COLUMN ); + bool bColRange = getFlag( nFlags, OOBIN_TOK_TABLE_COLRANGE ); + bool bValidRef = !bSingleCol || !bColRange; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - illegal combination of single column and column range" ); + if( bValidRef ) + { + if( bSingleCol ) + nStartCol = nEndCol = nCol1; + else if( bColRange ) + { nStartCol = nCol1; nEndCol = nCol2; } + bValidRef = (nStartCol <= nEndCol) && (nEndCol < nWidth); + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid column range" ); + } + + if( bValidRef ) + { + bool bAllRows = getFlag( nFlags, OOBIN_TOK_TABLE_ALL ); + bool bHeaderRows = getFlag( nFlags, OOBIN_TOK_TABLE_HEADERS ); + bool bDataRows = getFlag( nFlags, OOBIN_TOK_TABLE_DATA ); + bool bTotalsRows = getFlag( nFlags, OOBIN_TOK_TABLE_TOTALS ); + bool bThisRow = getFlag( nFlags, OOBIN_TOK_TABLE_THISROW ); + + sal_Int32 nStartDataRow = xTable->getHeaderRows(); + sal_Int32 nEndDataRow = nEndRow - xTable->getTotalsRows(); + bValidRef = (nStartRow <= nStartDataRow) && (nStartDataRow <= nEndDataRow) && (nEndDataRow <= nEndRow); + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid data row range" ); + if( bValidRef ) + { + if( bAllRows ) + { + bValidRef = !bHeaderRows && !bDataRows && !bTotalsRows && !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#All] table token" ); + } + else if( bHeaderRows ) + { + bValidRef = !bTotalsRows && !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Headers] table token" ); + nEndRow = bDataRows ? nEndDataRow : (nStartDataRow - 1); + bFixedHeight = !bDataRows; + } + else if( bDataRows ) + { + bValidRef = !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Data] table token" ); + nStartRow = nStartDataRow; + if( !bTotalsRows ) nEndRow = nEndDataRow; + } + else if( bTotalsRows ) + { + bValidRef = !bThisRow; + OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Totals] table token" ); + nStartRow = nEndDataRow + 1; + bFixedStartRow = false; + bFixedHeight = !bDataRows; + } + else if( bThisRow ) + { + nStartRow = nEndRow = getFormulaContext().getBaseAddress().Row - xTable->getRange().StartRow; + bFixedHeight = true; + } + else + { + // nothing is the same as [#Data] + nStartRow = nStartDataRow; + nEndRow = nEndDataRow; + } + } + if( bValidRef ) + bValidRef = (0 <= nStartRow) && (nStartRow <= nEndRow) && (nEndRow < nHeight); + } + if( bValidRef ) + { + // push single database area token, if table token refers to entire table + if( (nStartCol == 0) && (nEndCol + 1 == nWidth) && (nStartRow == 0) && (nEndRow + 1 == nHeight) ) + return pushValueOperand( nTokenIndex, mrFuncProv.OPCODE_DBAREA ); + // create an OFFSET function call to refer to a subrange of the table + const FunctionInfo* pRowsInfo = mrFuncProv.getFuncInfoFromOobFuncId( OOBIN_FUNC_ROWS ); + const FunctionInfo* pColumnsInfo = mrFuncProv.getFuncInfoFromOobFuncId( OOBIN_FUNC_COLUMNS ); + return + pRowsInfo && pColumnsInfo && + pushValueOperandToken( nTokenIndex, mrFuncProv.OPCODE_DBAREA, 0 ) && + (bFixedStartRow ? + pushValueOperandToken< double >( nStartRow, 0 ) : + (pushValueOperandToken( nTokenIndex, mrFuncProv.OPCODE_DBAREA, 0 ) && + pushFunctionOperatorToken( *pRowsInfo, 1, 0, 0 ) && + pushValueOperandToken< double >( nHeight - nStartRow, 0 ) && + pushBinaryOperatorToken( mrFuncProv.OPCODE_SUB, 0 ))) && + pushValueOperandToken< double >( nStartCol, 0 ) && + (bFixedHeight ? + pushValueOperandToken< double >( nEndRow - nStartRow + 1, 0 ) : + (pushValueOperandToken( nTokenIndex, mrFuncProv.OPCODE_DBAREA, 0 ) && + pushFunctionOperatorToken( *pRowsInfo, 1, 0, 0 ) && + (((nStartRow == 0) && (nEndRow + 1 == nHeight)) || + (pushValueOperandToken< double >( nHeight - (nEndRow - nStartRow + 1), 0 ) && + pushBinaryOperatorToken( mrFuncProv.OPCODE_SUB, 0 ))))) && + (((nStartCol == 0) && (nEndCol + 1 == nWidth)) ? + (pushValueOperandToken( nTokenIndex, mrFuncProv.OPCODE_DBAREA, 0 ) && + pushFunctionOperatorToken( *pColumnsInfo, 1, 0, 0 )) : + pushValueOperandToken< double >( nEndCol - nStartCol + 1, 0 )) && + pushOobFunction( OOBIN_FUNC_OFFSET, 5 ); + } + } + return pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool OoxFormulaParserImpl::importArrayToken( RecordInputStream& rStrm ) +{ + rStrm.skip( 14 ); + + // start token array with opening brace and leading spaces + pushOperand( mrFuncProv.OPCODE_ARRAY_OPEN ); + size_t nOpSize = popOperandSize(); + size_t nOldArraySize = getFormulaSize(); + + // read array size + swapStreamPosition( rStrm ); + sal_Int32 nRows = rStrm.readInt32(); + sal_Int32 nCols = rStrm.readInt32(); + OSL_ENSURE( (nCols > 0) && (nRows > 0), "OoxFormulaParserImpl::importArrayToken - empty array" ); + + // read array values and build token array + for( sal_Int32 nRow = 0; rStrm.isValid() && (nRow < nRows); ++nRow ) + { + if( nRow > 0 ) + appendRawToken( mrFuncProv.OPCODE_ARRAY_ROWSEP ); + for( sal_Int32 nCol = 0; rStrm.isValid() && (nCol < nCols); ++nCol ) + { + if( nCol > 0 ) + appendRawToken( mrFuncProv.OPCODE_ARRAY_COLSEP ); + switch( rStrm.readuInt8() ) + { + case OOBIN_TOK_ARRAY_DOUBLE: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= rStrm.readDouble(); + break; + case OOBIN_TOK_ARRAY_STRING: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= rStrm.readString( false ); + break; + case OOBIN_TOK_ARRAY_BOOL: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= static_cast< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 ); + break; + case OOBIN_TOK_ARRAY_ERROR: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( rStrm.readuInt8() ); + rStrm.skip( 3 ); + break; + default: + OSL_ENSURE( false, "OoxFormulaParserImpl::importArrayToken - unknown data type" ); + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NA ); + } + } + } + swapStreamPosition( rStrm ); + + // close token array and set resulting operand size + appendRawToken( mrFuncProv.OPCODE_ARRAY_CLOSE ); + pushOperandSize( nOpSize + getFormulaSize() - nOldArraySize ); + return true; +} + +bool OoxFormulaParserImpl::importRefToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinSingleRef2d aRef; + aRef.readOobData( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importAreaToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinComplexRef2d aRef; + aRef.readOobData( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importRef3dToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange( rStrm ); + BinSingleRef2d aRef; + aRef.readOobData( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importArea3dToken( RecordInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange( rStrm ); + BinComplexRef2d aRef; + aRef.readOobData( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool OoxFormulaParserImpl::importMemAreaToken( RecordInputStream& rStrm, bool bAddData ) +{ + rStrm.skip( 6 ); + if( bAddData ) + skipMemAreaAddData( rStrm ); + return true; +} + +bool OoxFormulaParserImpl::importMemFuncToken( RecordInputStream& rStrm ) +{ + rStrm.skip( 2 ); + return true; +} + +bool OoxFormulaParserImpl::importNameToken( RecordInputStream& rStrm ) +{ + return pushOobName( rStrm.readInt32() ); +} + +bool OoxFormulaParserImpl::importNameXToken( RecordInputStream& rStrm ) +{ + sal_Int32 nRefId = rStrm.readInt16(); + sal_Int32 nNameId = rStrm.readInt32(); + return pushOobExtName( nRefId, nNameId ); +} + +bool OoxFormulaParserImpl::importFuncToken( RecordInputStream& rStrm ) +{ + sal_uInt16 nFuncId; + rStrm >> nFuncId; + return pushOobFunction( nFuncId ); +} + +bool OoxFormulaParserImpl::importFuncVarToken( RecordInputStream& rStrm ) +{ + sal_uInt8 nParamCount; + sal_uInt16 nFuncId; + rStrm >> nParamCount >> nFuncId; + return pushOobFunction( nFuncId, nParamCount ); +} + +bool OoxFormulaParserImpl::importExpToken( RecordInputStream& rStrm ) +{ + BinAddress aBaseAddr; + rStrm >> aBaseAddr.mnRow; + swapStreamPosition( rStrm ); + rStrm >> aBaseAddr.mnCol; + swapStreamPosition( rStrm ); + setSharedFormula( aBaseAddr ); + // formula has been set, exit parser by returning false + return false; +} + +LinkSheetRange OoxFormulaParserImpl::readSheetRange( RecordInputStream& rStrm ) +{ + return getExternalLinks().getSheetRange( rStrm.readInt16() ); +} + +void OoxFormulaParserImpl::swapStreamPosition( RecordInputStream& rStrm ) +{ + sal_Int32 nRecPos = rStrm.getRecPos(); + rStrm.seek( mnAddDataPos ); + mnAddDataPos = nRecPos; +} + +void OoxFormulaParserImpl::skipMemAreaAddData( RecordInputStream& rStrm ) +{ + swapStreamPosition( rStrm ); + rStrm.skip( 16 * rStrm.readInt32() ); + swapStreamPosition( rStrm ); +} + +// convert BIN token and push API operand or operator ------------------------- + +bool OoxFormulaParserImpl::pushOobName( sal_Int32 nNameId ) +{ + // one-based in OOBIN formulas + return pushDefinedNameOperand( getDefinedNames().getByIndex( nNameId - 1 ) ); +} + +bool OoxFormulaParserImpl::pushOobExtName( sal_Int32 nRefId, sal_Int32 nNameId ) +{ + if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() ) + { + if( pExtLink->getLinkType() == LINKTYPE_SELF ) + return pushOobName( nNameId ); + // external name indexes are one-based in OOBIN + ExternalNameRef xExtName = pExtLink->getNameByIndex( nNameId - 1 ); + return pushExternalNameOperand( xExtName, pExtLink->getLinkType() ); + } + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool OoxFormulaParserImpl::pushOobFunction( sal_uInt16 nFuncId ) +{ + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromOobFuncId( nFuncId ) ) + if( pFuncInfo->mnMinParamCount == pFuncInfo->mnMaxParamCount ) + return pushFunctionOperator( *pFuncInfo, pFuncInfo->mnMinParamCount ); + return pushFunctionOperator( mrFuncProv.OPCODE_NONAME, 0 ); +} + +bool OoxFormulaParserImpl::pushOobFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ) +{ + if( getFlag( nFuncId, BIFF_TOK_FUNCVAR_CMD ) ) + nParamCount &= BIFF_TOK_FUNCVAR_COUNTMASK; + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromOobFuncId( nFuncId ) ) + return pushFunctionOperator( *pFuncInfo, nParamCount ); + return pushFunctionOperator( mrFuncProv.OPCODE_NONAME, nParamCount ); +} + +// BIFF parser implementation ================================================= + +namespace { + +/** A natural language reference struct with relative flag. */ +struct BiffNlr +{ + sal_Int32 mnCol; /// Column index. + sal_Int32 mnRow; /// Row index. + bool mbRel; /// True = relative column/row reference. + + explicit BiffNlr(); + + void readBiff8Data( BiffInputStream& rStrm ); +}; + +BiffNlr::BiffNlr() : + mnCol( 0 ), + mnRow( 0 ), + mbRel( false ) +{ +} + +void BiffNlr::readBiff8Data( BiffInputStream& rStrm ) +{ + sal_uInt16 nRow, nCol; + rStrm >> nRow >> nCol; + mnCol = nCol & BIFF_TOK_NLR_MASK; + mnRow = nRow; + mbRel = getFlag( nCol, BIFF_TOK_NLR_REL ); +} + +bool lclIsValidNlrStack( const BinAddress& rAddr1, const BinAddress& rAddr2, bool bRow ) +{ + return bRow ? + ((rAddr1.mnRow == rAddr2.mnRow) && (rAddr1.mnCol + 1 == rAddr2.mnCol)) : + ((rAddr1.mnCol == rAddr2.mnCol) && (rAddr1.mnRow + 1 == rAddr2.mnRow)); +} + +bool lclIsValidNlrRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow ) +{ + return bRow ? + ((rNlr.mnRow == rRange.maFirst.mnRow) && (rNlr.mnCol + 1 == rRange.maFirst.mnCol) && (rRange.maFirst.mnRow == rRange.maLast.mnRow)) : + ((rNlr.mnCol == rRange.maFirst.mnCol) && (rNlr.mnRow + 1 == rRange.maFirst.mnRow) && (rRange.maFirst.mnCol == rRange.maLast.mnCol)); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +class BiffFormulaParserImpl : public FormulaParserImpl +{ +public: + explicit BiffFormulaParserImpl( + const WorkbookHelper& rHelper, + const FunctionProvider& rFuncProv ); + + virtual void importBiffFormula( + FormulaContext& rContext, + BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ); + +private: + // import token contents and create API formula token --------------------- + + bool importTokenNotAvailable( BiffInputStream& rStrm ); + bool importRefTokenNotAvailable( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importStrToken2( BiffInputStream& rStrm ); + bool importStrToken8( BiffInputStream& rStrm ); + bool importAttrToken( BiffInputStream& rStrm ); + bool importSpaceToken3( BiffInputStream& rStrm ); + bool importSpaceToken4( BiffInputStream& rStrm ); + bool importSheetToken2( BiffInputStream& rStrm ); + bool importSheetToken3( BiffInputStream& rStrm ); + bool importEndSheetToken2( BiffInputStream& rStrm ); + bool importEndSheetToken3( BiffInputStream& rStrm ); + bool importNlrToken( BiffInputStream& rStrm ); + bool importArrayToken( BiffInputStream& rStrm ); + bool importRefToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRefToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importAreaToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importAreaToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRef3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importRef3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importArea3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importArea3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ); + bool importMemAreaToken( BiffInputStream& rStrm, bool bAddData ); + bool importMemFuncToken( BiffInputStream& rStrm ); + bool importNameToken( BiffInputStream& rStrm ); + bool importNameXToken( BiffInputStream& rStrm ); + bool importFuncToken2( BiffInputStream& rStrm ); + bool importFuncToken4( BiffInputStream& rStrm ); + bool importFuncVarToken2( BiffInputStream& rStrm ); + bool importFuncVarToken4( BiffInputStream& rStrm ); + bool importFuncCEToken( BiffInputStream& rStrm ); + bool importExpToken5( BiffInputStream& rStrm ); + + bool importNlrAddrToken( BiffInputStream& rStrm, bool bRow ); + bool importNlrRangeToken( BiffInputStream& rStrm ); + bool importNlrSAddrToken( BiffInputStream& rStrm, bool bRow ); + bool importNlrSRangeToken( BiffInputStream& rStrm ); + bool importNlrErrToken( BiffInputStream& rStrm, sal_uInt16 nSkip ); + + sal_Int32 readRefId( BiffInputStream& rStrm ); + sal_uInt16 readNameId( BiffInputStream& rStrm ); + LinkSheetRange readSheetRange5( BiffInputStream& rStrm ); + LinkSheetRange readSheetRange8( BiffInputStream& rStrm ); + + void swapStreamPosition( BiffInputStream& rStrm ); + void skipMemAreaAddData( BiffInputStream& rStrm ); + bool readNlrSAddrAddData( BiffNlr& orNlr, BiffInputStream& rStrm, bool bRow ); + bool readNlrSRangeAddData( BiffNlr& orNlr, bool& orbIsRow, BiffInputStream& rStrm ); + + // convert BIFF token and push API operand or operator -------------------- + + bool pushBiffReference( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushBiffReference( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ); + bool pushBiffNlrAddr( const BiffNlr& rNlr, bool bRow ); + bool pushBiffNlrRange( const BiffNlr& rNlr, const BinRange& rRange ); + bool pushBiffNlrSAddr( const BiffNlr& rNlr, bool bRow ); + bool pushBiffNlrSRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow ); + bool pushBiffName( sal_uInt16 nNameId ); + bool pushBiffExtName( sal_Int32 nRefId, sal_uInt16 nNameId ); + bool pushBiffFunction( sal_uInt16 nFuncId ); + bool pushBiffFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ); + + // ------------------------------------------------------------------------ +private: + typedef bool (BiffFormulaParserImpl::*ImportTokenFunc)( BiffInputStream& ); + typedef bool (BiffFormulaParserImpl::*ImportRefTokenFunc)( BiffInputStream&, bool, bool ); + + ImportTokenFunc mpImportStrToken; /// Pointer to tStr import function (string constant). + ImportTokenFunc mpImportSpaceToken; /// Pointer to tAttrSpace import function (spaces/line breaks). + ImportTokenFunc mpImportSheetToken; /// Pointer to tSheet import function (external reference). + ImportTokenFunc mpImportEndSheetToken; /// Pointer to tEndSheet import function (end of external reference). + ImportTokenFunc mpImportNlrToken; /// Pointer to tNlr import function (natural language reference). + ImportRefTokenFunc mpImportRefToken; /// Pointer to tRef import function (2d cell reference). + ImportRefTokenFunc mpImportAreaToken; /// Pointer to tArea import function (2d area reference). + ImportRefTokenFunc mpImportRef3dToken; /// Pointer to tRef3d import function (3d cell reference). + ImportRefTokenFunc mpImportArea3dToken; /// Pointer to tArea3d import function (3d area reference). + ImportTokenFunc mpImportNameXToken; /// Pointer to tNameX import function (external name). + ImportTokenFunc mpImportFuncToken; /// Pointer to tFunc import function (function with fixed parameter count). + ImportTokenFunc mpImportFuncVarToken; /// Pointer to tFuncVar import function (function with variable parameter count). + ImportTokenFunc mpImportFuncCEToken; /// Pointer to tFuncCE import function (command macro call). + ImportTokenFunc mpImportExpToken; /// Pointer to tExp import function (array/shared formula). + sal_uInt32 mnAddDataPos; /// Current stream position for additional data (tArray, tMemArea, tNlr). + sal_Int32 mnCurrRefId; /// Current ref-id from tSheet token (BIFF2-BIFF4 only). + sal_uInt16 mnAttrDataSize; /// Size of one tAttr data element. + sal_uInt16 mnArraySize; /// Size of tArray data. + sal_uInt16 mnNameSize; /// Size of tName data. + sal_uInt16 mnMemAreaSize; /// Size of tMemArea data. + sal_uInt16 mnMemFuncSize; /// Size of tMemFunc data. + sal_uInt16 mnRefIdSize; /// Size of unused data following a reference identifier. +}; + +// ---------------------------------------------------------------------------- + +BiffFormulaParserImpl::BiffFormulaParserImpl( const WorkbookHelper& rHelper, const FunctionProvider& rFuncProv ) : + FormulaParserImpl( rHelper, rFuncProv ), + mnAddDataPos( 0 ), + mnCurrRefId( 0 ) +{ + switch( getBiff() ) + { + case BIFF2: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportSheetToken = &BiffFormulaParserImpl::importSheetToken2; + mpImportEndSheetToken = &BiffFormulaParserImpl::importEndSheetToken2; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportArea3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportNameXToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken2; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken2; + mpImportFuncCEToken = &BiffFormulaParserImpl::importFuncCEToken; + mpImportExpToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mnAttrDataSize = 1; + mnArraySize = 6; + mnNameSize = 5; + mnMemAreaSize = 4; + mnMemFuncSize = 1; + mnRefIdSize = 1; + break; + case BIFF3: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken3; + mpImportSheetToken = &BiffFormulaParserImpl::importSheetToken3; + mpImportEndSheetToken = &BiffFormulaParserImpl::importEndSheetToken3; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportArea3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportNameXToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken2; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken2; + mpImportFuncCEToken = &BiffFormulaParserImpl::importFuncCEToken; + mpImportExpToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 8; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 2; + break; + case BIFF4: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken4; + mpImportSheetToken = &BiffFormulaParserImpl::importSheetToken3; + mpImportEndSheetToken = &BiffFormulaParserImpl::importEndSheetToken3; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportArea3dToken = &BiffFormulaParserImpl::importRefTokenNotAvailable; + mpImportNameXToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken4; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken4; + mpImportFuncCEToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportExpToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 8; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 2; + break; + case BIFF5: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken2; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken4; + mpImportSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportEndSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportNlrToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken2; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken2; + mpImportRef3dToken = &BiffFormulaParserImpl::importRef3dToken5; + mpImportArea3dToken = &BiffFormulaParserImpl::importArea3dToken5; + mpImportNameXToken = &BiffFormulaParserImpl::importNameXToken; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken4; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken4; + mpImportFuncCEToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportExpToken = &BiffFormulaParserImpl::importExpToken5; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 12; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 8; + break; + case BIFF8: + mpImportStrToken = &BiffFormulaParserImpl::importStrToken8; + mpImportSpaceToken = &BiffFormulaParserImpl::importSpaceToken4; + mpImportSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportEndSheetToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportNlrToken = &BiffFormulaParserImpl::importNlrToken; + mpImportRefToken = &BiffFormulaParserImpl::importRefToken8; + mpImportAreaToken = &BiffFormulaParserImpl::importAreaToken8; + mpImportRef3dToken = &BiffFormulaParserImpl::importRef3dToken8; + mpImportArea3dToken = &BiffFormulaParserImpl::importArea3dToken8; + mpImportNameXToken = &BiffFormulaParserImpl::importNameXToken; + mpImportFuncToken = &BiffFormulaParserImpl::importFuncToken4; + mpImportFuncVarToken = &BiffFormulaParserImpl::importFuncVarToken4; + mpImportFuncCEToken = &BiffFormulaParserImpl::importTokenNotAvailable; + mpImportExpToken = &BiffFormulaParserImpl::importExpToken5; + mnAttrDataSize = 2; + mnArraySize = 7; + mnNameSize = 2; + mnMemAreaSize = 6; + mnMemFuncSize = 2; + mnRefIdSize = 0; + break; + case BIFF_UNKNOWN: break; + } +} + +void BiffFormulaParserImpl::importBiffFormula( FormulaContext& rContext, + BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ) +{ + initializeImport( rContext ); + mnCurrRefId = 0; + + sal_uInt16 nFmlaSize = pnFmlaSize ? *pnFmlaSize : ((getBiff() == BIFF2) ? rStrm.readuInt8() : rStrm.readuInt16()); + sal_uInt32 nEndPos = mnAddDataPos = rStrm.getRecPos() + nFmlaSize; + bool bRelativeAsOffset = getFormulaContext().isRelativeAsOffset(); + + bool bOk = true; + while( bOk && rStrm.isValid() && (rStrm.getRecPos() < nEndPos) ) + { + sal_uInt8 nTokenId; + rStrm >> nTokenId; + sal_uInt8 nTokenClass = nTokenId & BIFF_TOKCLASS_MASK; + sal_uInt8 nBaseId = nTokenId & BIFF_TOKID_MASK; + + if( nTokenClass == BIFF_TOKCLASS_NONE ) + { + // base tokens + switch( nBaseId ) + { + case BIFF_TOKID_EXP: bOk = (this->*mpImportExpToken)( rStrm ); break; + case BIFF_TOKID_TBL: bOk = false; /* multiple op. will be set externally */ break; + case BIFF_TOKID_ADD: bOk = pushBinaryOperator( mrFuncProv.OPCODE_ADD ); break; + case BIFF_TOKID_SUB: bOk = pushBinaryOperator( mrFuncProv.OPCODE_SUB ); break; + case BIFF_TOKID_MUL: bOk = pushBinaryOperator( mrFuncProv.OPCODE_MULT ); break; + case BIFF_TOKID_DIV: bOk = pushBinaryOperator( mrFuncProv.OPCODE_DIV ); break; + case BIFF_TOKID_POWER: bOk = pushBinaryOperator( mrFuncProv.OPCODE_POWER ); break; + case BIFF_TOKID_CONCAT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_CONCAT ); break; + case BIFF_TOKID_LT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_LESS ); break; + case BIFF_TOKID_LE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_LESS_EQUAL ); break; + case BIFF_TOKID_EQ: bOk = pushBinaryOperator( mrFuncProv.OPCODE_EQUAL ); break; + case BIFF_TOKID_GE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_GREATER_EQUAL ); break; + case BIFF_TOKID_GT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_GREATER ); break; + case BIFF_TOKID_NE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_NOT_EQUAL ); break; + case BIFF_TOKID_ISECT: bOk = pushBinaryOperator( mrFuncProv.OPCODE_INTERSECT ); break; + case BIFF_TOKID_LIST: bOk = pushBinaryOperator( mrFuncProv.OPCODE_LIST ); break; + case BIFF_TOKID_RANGE: bOk = pushBinaryOperator( mrFuncProv.OPCODE_RANGE ); break; + case BIFF_TOKID_UPLUS: bOk = pushUnaryPreOperator( mrFuncProv.OPCODE_PLUS_SIGN ); break; + case BIFF_TOKID_UMINUS: bOk = pushUnaryPreOperator( mrFuncProv.OPCODE_MINUS_SIGN ); break; + case BIFF_TOKID_PERCENT: bOk = pushUnaryPostOperator( mrFuncProv.OPCODE_PERCENT ); break; + case BIFF_TOKID_PAREN: bOk = pushParenthesesOperator(); break; + case BIFF_TOKID_MISSARG: bOk = pushOperand( mrFuncProv.OPCODE_MISSING ); break; + case BIFF_TOKID_STR: bOk = (this->*mpImportStrToken)( rStrm ); break; + case BIFF_TOKID_NLR: bOk = (this->*mpImportNlrToken)( rStrm ); break; + case BIFF_TOKID_ATTR: bOk = importAttrToken( rStrm ); break; + case BIFF_TOKID_SHEET: bOk = (this->*mpImportSheetToken)( rStrm ); break; + case BIFF_TOKID_ENDSHEET: bOk = (this->*mpImportEndSheetToken)( rStrm ); break; + case BIFF_TOKID_ERR: bOk = pushBiffErrorOperand( rStrm.readuInt8() ); break; + case BIFF_TOKID_BOOL: bOk = pushBoolOperand( rStrm.readuInt8() != BIFF_TOK_BOOL_FALSE ); break; + case BIFF_TOKID_INT: bOk = pushValueOperand< double >( rStrm.readuInt16() ); break; + case BIFF_TOKID_NUM: bOk = pushValueOperand( rStrm.readDouble() ); break; + default: bOk = false; + } + } + else + { + // classified tokens + switch( nBaseId ) + { + case BIFF_TOKID_ARRAY: bOk = importArrayToken( rStrm ); break; + case BIFF_TOKID_FUNC: bOk = (this->*mpImportFuncToken)( rStrm ); break; + case BIFF_TOKID_FUNCVAR: bOk = (this->*mpImportFuncVarToken)( rStrm ); break; + case BIFF_TOKID_NAME: bOk = importNameToken( rStrm ); break; + case BIFF_TOKID_REF: bOk = (this->*mpImportRefToken)( rStrm, false, false ); break; + case BIFF_TOKID_AREA: bOk = (this->*mpImportAreaToken)( rStrm, false, false ); break; + case BIFF_TOKID_MEMAREA: bOk = importMemAreaToken( rStrm, true ); break; + case BIFF_TOKID_MEMERR: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMNOMEM: bOk = importMemAreaToken( rStrm, false ); break; + case BIFF_TOKID_MEMFUNC: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_REFERR: bOk = (this->*mpImportRefToken)( rStrm, true, false ); break; + case BIFF_TOKID_AREAERR: bOk = (this->*mpImportAreaToken)( rStrm, true, false ); break; + case BIFF_TOKID_REFN: bOk = (this->*mpImportRefToken)( rStrm, false, true ); break; + case BIFF_TOKID_AREAN: bOk = (this->*mpImportAreaToken)( rStrm, false, true ); break; + case BIFF_TOKID_MEMAREAN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_MEMNOMEMN: bOk = importMemFuncToken( rStrm ); break; + case BIFF_TOKID_FUNCCE: bOk = (this->*mpImportFuncCEToken)( rStrm ); break; + case BIFF_TOKID_NAMEX: bOk = (this->*mpImportNameXToken)( rStrm ); break; + case BIFF_TOKID_REF3D: bOk = (this->*mpImportRef3dToken)( rStrm, false, bRelativeAsOffset ); break; + case BIFF_TOKID_AREA3D: bOk = (this->*mpImportArea3dToken)( rStrm, false, bRelativeAsOffset ); break; + case BIFF_TOKID_REFERR3D: bOk = (this->*mpImportRef3dToken)( rStrm, true, bRelativeAsOffset ); break; + case BIFF_TOKID_AREAERR3D: bOk = (this->*mpImportArea3dToken)( rStrm, true, bRelativeAsOffset ); break; + default: bOk = false; + } + } + } + + // build and finalize the token sequence + if( bOk && (rStrm.getRecPos() == nEndPos) ) + finalizeImport(); + + // seek behind additional token data of tArray, tMemArea, tNlr tokens + rStrm.seek( mnAddDataPos ); +} + +// import token contents and create API formula token ------------------------- + +bool BiffFormulaParserImpl::importTokenNotAvailable( BiffInputStream& ) +{ + // dummy function for pointer-to-member-function + return false; +} + +bool BiffFormulaParserImpl::importRefTokenNotAvailable( BiffInputStream&, bool, bool ) +{ + // dummy function for pointer-to-member-function + return false; +} + +bool BiffFormulaParserImpl::importStrToken2( BiffInputStream& rStrm ) +{ + return pushValueOperand( rStrm.readByteString( false, getTextEncoding() ) ); +} + +bool BiffFormulaParserImpl::importStrToken8( BiffInputStream& rStrm ) +{ + // read flags field for empty strings also + return pushValueOperand( rStrm.readUniString( rStrm.readuInt8() ) ); +} + +bool BiffFormulaParserImpl::importAttrToken( BiffInputStream& rStrm ) +{ + bool bOk = true; + sal_uInt8 nType; + rStrm >> nType; + switch( nType ) + { + case BIFF_TOK_ATTR_VOLATILE: + case BIFF_TOK_ATTR_IF: + case BIFF_TOK_ATTR_SKIP: + case BIFF_TOK_ATTR_ASSIGN: + rStrm.skip( mnAttrDataSize ); + break; + case BIFF_TOK_ATTR_CHOOSE: + rStrm.skip( mnAttrDataSize * (1 + ((getBiff() == BIFF2) ? rStrm.readuInt8() : rStrm.readuInt16())) ); + break; + case BIFF_TOK_ATTR_SUM: + rStrm.skip( mnAttrDataSize ); + bOk = pushBiffFunction( BIFF_FUNC_SUM, 1 ); + break; + case BIFF_TOK_ATTR_SPACE: + case BIFF_TOK_ATTR_SPACE_VOLATILE: + bOk = (this->*mpImportSpaceToken)( rStrm ); + break; + default: + bOk = false; + } + return bOk; +} + +bool BiffFormulaParserImpl::importSpaceToken3( BiffInputStream& rStrm ) +{ + rStrm.skip( 2 ); + return true; +} + +bool BiffFormulaParserImpl::importSpaceToken4( BiffInputStream& rStrm ) +{ + sal_uInt8 nType, nCount; + rStrm >> nType >> nCount; + switch( nType ) + { + case BIFF_TOK_ATTR_SPACE_SP: + case BIFF_TOK_ATTR_SPACE_BR: + incLeadingSpaces( nCount ); + break; + case BIFF_TOK_ATTR_SPACE_SP_OPEN: + case BIFF_TOK_ATTR_SPACE_BR_OPEN: + incOpeningSpaces( nCount ); + break; + case BIFF_TOK_ATTR_SPACE_SP_CLOSE: + case BIFF_TOK_ATTR_SPACE_BR_CLOSE: + incClosingSpaces( nCount ); + break; + } + return true; +} + +bool BiffFormulaParserImpl::importSheetToken2( BiffInputStream& rStrm ) +{ + rStrm.skip( 4 ); + mnCurrRefId = readRefId( rStrm ); + return true; +} + +bool BiffFormulaParserImpl::importSheetToken3( BiffInputStream& rStrm ) +{ + rStrm.skip( 6 ); + mnCurrRefId = readRefId( rStrm ); + return true; +} + +bool BiffFormulaParserImpl::importEndSheetToken2( BiffInputStream& rStrm ) +{ + rStrm.skip( 3 ); + mnCurrRefId = 0; + return true; +} + +bool BiffFormulaParserImpl::importEndSheetToken3( BiffInputStream& rStrm ) +{ + rStrm.skip( 4 ); + mnCurrRefId = 0; + return true; +} + +bool BiffFormulaParserImpl::importNlrToken( BiffInputStream& rStrm ) +{ + bool bOk = true; + sal_uInt8 nNlrType; + rStrm >> nNlrType; + switch( nNlrType ) + { + case BIFF_TOK_NLR_ERR: bOk = importNlrErrToken( rStrm, 4 ); break; + case BIFF_TOK_NLR_ROWR: bOk = importNlrAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_COLR: bOk = importNlrAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_ROWV: bOk = importNlrAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_COLV: bOk = importNlrAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_RANGE: bOk = importNlrRangeToken( rStrm ); break; + case BIFF_TOK_NLR_SRANGE: bOk = importNlrSRangeToken( rStrm ); break; + case BIFF_TOK_NLR_SROWR: bOk = importNlrSAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_SCOLR: bOk = importNlrSAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_SROWV: bOk = importNlrSAddrToken( rStrm, true ); break; + case BIFF_TOK_NLR_SCOLV: bOk = importNlrSAddrToken( rStrm, false ); break; + case BIFF_TOK_NLR_RANGEERR: bOk = importNlrErrToken( rStrm, 13 ); break; + case BIFF_TOK_NLR_SXNAME: bOk = importNlrErrToken( rStrm, 4 ); break; + default: bOk = false; + } + return bOk; +} + +bool BiffFormulaParserImpl::importArrayToken( BiffInputStream& rStrm ) +{ + rStrm.skip( mnArraySize ); + + // start token array with opening brace and leading spaces + pushOperand( mrFuncProv.OPCODE_ARRAY_OPEN ); + size_t nOpSize = popOperandSize(); + size_t nOldArraySize = getFormulaSize(); + bool bBiff8 = getBiff() == BIFF8; + + // read array size + swapStreamPosition( rStrm ); + sal_uInt16 nCols = rStrm.readuInt8(); + sal_uInt16 nRows = rStrm.readuInt16(); + if( bBiff8 ) { ++nCols; ++nRows; } else if( nCols == 0 ) nCols = 256; + OSL_ENSURE( (nCols > 0) && (nRows > 0), "BiffFormulaParserImpl::importArrayToken - empty array" ); + + // read array values and build token array + for( sal_uInt16 nRow = 0; rStrm.isValid() && (nRow < nRows); ++nRow ) + { + if( nRow > 0 ) + appendRawToken( mrFuncProv.OPCODE_ARRAY_ROWSEP ); + for( sal_uInt16 nCol = 0; rStrm.isValid() && (nCol < nCols); ++nCol ) + { + if( nCol > 0 ) + appendRawToken( mrFuncProv.OPCODE_ARRAY_COLSEP ); + switch( rStrm.readuInt8() ) + { + case BIFF_DATATYPE_EMPTY: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= OUString(); + rStrm.skip( 8 ); + break; + case BIFF_DATATYPE_DOUBLE: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= rStrm.readDouble(); + break; + case BIFF_DATATYPE_STRING: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= bBiff8 ? + rStrm.readUniString() : + rStrm.readByteString( false, getTextEncoding() ); + break; + case BIFF_DATATYPE_BOOL: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= static_cast< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 ); + rStrm.skip( 7 ); + break; + case BIFF_DATATYPE_ERROR: + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( rStrm.readuInt8() ); + rStrm.skip( 7 ); + break; + default: + OSL_ENSURE( false, "BiffFormulaParserImpl::importArrayToken - unknown data type" ); + appendRawToken( mrFuncProv.OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NA ); + } + } + } + swapStreamPosition( rStrm ); + + // close token array and set resulting operand size + appendRawToken( mrFuncProv.OPCODE_ARRAY_CLOSE ); + pushOperandSize( nOpSize + getFormulaSize() - nOldArraySize ); + return true; +} + +bool BiffFormulaParserImpl::importRefToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinSingleRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importRefToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinSingleRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importAreaToken2( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinComplexRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importAreaToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + BinComplexRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushBiffReference( aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importRef3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange5( rStrm ); + BinSingleRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importRef3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange8( rStrm ); + BinSingleRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importArea3dToken5( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange5( rStrm ); + BinComplexRef2d aRef; + aRef.readBiff2Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importArea3dToken8( BiffInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset ) +{ + LinkSheetRange aSheetRange = readSheetRange8( rStrm ); + BinComplexRef2d aRef; + aRef.readBiff8Data( rStrm, bRelativeAsOffset ); + return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::importMemAreaToken( BiffInputStream& rStrm, bool bAddData ) +{ + rStrm.skip( mnMemAreaSize ); + if( bAddData ) + skipMemAreaAddData( rStrm ); + return true; +} + +bool BiffFormulaParserImpl::importMemFuncToken( BiffInputStream& rStrm ) +{ + rStrm.skip( mnMemFuncSize ); + return true; +} + +bool BiffFormulaParserImpl::importNameToken( BiffInputStream& rStrm ) +{ + sal_uInt16 nNameId = readNameId( rStrm ); + return (mnCurrRefId > 0) ? pushBiffExtName( mnCurrRefId, nNameId ) : pushBiffName( nNameId ); +} + +bool BiffFormulaParserImpl::importNameXToken( BiffInputStream& rStrm ) +{ + sal_Int32 nRefId = readRefId( rStrm ); + sal_uInt16 nNameId = readNameId( rStrm ); + return pushBiffExtName( nRefId, nNameId ); +} + +bool BiffFormulaParserImpl::importFuncToken2( BiffInputStream& rStrm ) +{ + sal_uInt8 nFuncId; + rStrm >> nFuncId; + return pushBiffFunction( nFuncId ); +} + +bool BiffFormulaParserImpl::importFuncToken4( BiffInputStream& rStrm ) +{ + sal_uInt16 nFuncId; + rStrm >> nFuncId; + return pushBiffFunction( nFuncId ); +} + +bool BiffFormulaParserImpl::importFuncVarToken2( BiffInputStream& rStrm ) +{ + sal_uInt8 nParamCount, nFuncId; + rStrm >> nParamCount >> nFuncId; + return pushBiffFunction( nFuncId, nParamCount ); +} + +bool BiffFormulaParserImpl::importFuncVarToken4( BiffInputStream& rStrm ) +{ + sal_uInt8 nParamCount; + sal_uInt16 nFuncId; + rStrm >> nParamCount >> nFuncId; + return pushBiffFunction( nFuncId, nParamCount & BIFF_TOK_FUNCVAR_COUNTMASK ); +} + +bool BiffFormulaParserImpl::importFuncCEToken( BiffInputStream& rStrm ) +{ + sal_uInt8 nParamCount, nFuncId; + rStrm >> nParamCount >> nFuncId; + sal_uInt16 nCmdId = nFuncId; + setFlag( nCmdId, BIFF_TOK_FUNCVAR_CMD ); + return pushBiffFunction( nCmdId, nParamCount ); +} + +bool BiffFormulaParserImpl::importExpToken5( BiffInputStream& rStrm ) +{ + BinAddress aBaseAddr; + aBaseAddr.read( rStrm ); + setSharedFormula( aBaseAddr ); + // formula has been set, exit parser by returning false + return false; +} + +bool BiffFormulaParserImpl::importNlrAddrToken( BiffInputStream& rStrm, bool bRow ) +{ + BiffNlr aNlr; + aNlr.readBiff8Data( rStrm ); + return pushBiffNlrAddr( aNlr, bRow ); +} + +bool BiffFormulaParserImpl::importNlrRangeToken( BiffInputStream& rStrm ) +{ + BiffNlr aNlr; + aNlr.readBiff8Data( rStrm ); + rStrm.skip( 1 ); + BinRange aRange; + rStrm >> aRange; + return pushBiffNlrRange( aNlr, aRange ); +} + +bool BiffFormulaParserImpl::importNlrSAddrToken( BiffInputStream& rStrm, bool bRow ) +{ + rStrm.skip( 4 ); + BiffNlr aNlr; + return readNlrSAddrAddData( aNlr, rStrm, bRow ) ? pushBiffNlrSAddr( aNlr, bRow ) : pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::importNlrSRangeToken( BiffInputStream& rStrm ) +{ + rStrm.skip( 5 ); + BinRange aRange; + rStrm >> aRange; + BiffNlr aNlr; + bool bRow; + return readNlrSRangeAddData( aNlr, bRow, rStrm ) ? pushBiffNlrSRange( aNlr, aRange, bRow ) : pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::importNlrErrToken( BiffInputStream& rStrm, sal_uInt16 nIgnore ) +{ + rStrm.skip( nIgnore ); + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +sal_Int32 BiffFormulaParserImpl::readRefId( BiffInputStream& rStrm ) +{ + sal_Int16 nRefId; + rStrm >> nRefId; + rStrm.skip( mnRefIdSize ); + return nRefId; +} + +sal_uInt16 BiffFormulaParserImpl::readNameId( BiffInputStream& rStrm ) +{ + sal_uInt16 nNameId; + rStrm >> nNameId; + rStrm.skip( mnNameSize ); + return nNameId; +} + +LinkSheetRange BiffFormulaParserImpl::readSheetRange5( BiffInputStream& rStrm ) +{ + sal_Int32 nRefId = readRefId( rStrm ); + sal_Int16 nTab1, nTab2; + rStrm >> nTab1 >> nTab2; + return getExternalLinks().getSheetRange( nRefId, nTab1, nTab2 ); +} + +LinkSheetRange BiffFormulaParserImpl::readSheetRange8( BiffInputStream& rStrm ) +{ + return getExternalLinks().getSheetRange( readRefId( rStrm ) ); +} + +void BiffFormulaParserImpl::swapStreamPosition( BiffInputStream& rStrm ) +{ + sal_uInt32 nRecPos = rStrm.getRecPos(); + rStrm.seek( mnAddDataPos ); + mnAddDataPos = nRecPos; +} + +void BiffFormulaParserImpl::skipMemAreaAddData( BiffInputStream& rStrm ) +{ + swapStreamPosition( rStrm ); + sal_uInt32 nCount = rStrm.readuInt16(); + rStrm.skip( ((getBiff() == BIFF8) ? 8 : 6) * nCount ); + swapStreamPosition( rStrm ); +} + +bool BiffFormulaParserImpl::readNlrSAddrAddData( BiffNlr& orNlr, BiffInputStream& rStrm, bool bRow ) +{ + bool bIsRow; + return readNlrSRangeAddData( orNlr, bIsRow, rStrm ) && (bIsRow == bRow); +} + +bool BiffFormulaParserImpl::readNlrSRangeAddData( BiffNlr& orNlr, bool& orbIsRow, BiffInputStream& rStrm ) +{ + swapStreamPosition( rStrm ); + // read number of cell addresses and relative flag + sal_uInt32 nCount; + rStrm >> nCount; + bool bRel = getFlag( nCount, BIFF_TOK_NLR_ADDREL ); + nCount &= BIFF_TOK_NLR_ADDMASK; + sal_uInt32 nEndPos = rStrm.getRecPos() + 4 * nCount; + // read list of cell addresses + bool bValid = false; + if( nCount >= 2 ) + { + // detect column/row orientation + BinAddress aAddr1, aAddr2; + rStrm >> aAddr1 >> aAddr2; + orbIsRow = aAddr1.mnRow == aAddr2.mnRow; + bValid = lclIsValidNlrStack( aAddr1, aAddr2, orbIsRow ); + // read and verify additional cell positions + for( sal_uInt32 nIndex = 2; bValid && (nIndex < nCount); ++nIndex ) + { + aAddr1 = aAddr2; + rStrm >> aAddr2; + bValid = rStrm.isValid() && lclIsValidNlrStack( aAddr1, aAddr2, orbIsRow ); + } + // check that last imported position (aAddr2) is not at the end of the sheet + bValid = bValid && (orbIsRow ? (aAddr2.mnCol < mnMaxApiCol) : (aAddr2.mnRow < mnMaxApiRow)); + // fill the NLR struct with the last imported position + if( bValid ) + { + orNlr.mnCol = aAddr2.mnCol; + orNlr.mnRow = aAddr2.mnRow; + orNlr.mbRel = bRel; + } + } + // seek to end of additional data for this token + rStrm.seek( nEndPos ); + swapStreamPosition( rStrm ); + + return bValid; +} + +// convert BIFF token and push API operand or operator ------------------------ + +bool BiffFormulaParserImpl::pushBiffReference( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + return (mnCurrRefId > 0) ? + pushReferenceOperand( getExternalLinks().getSheetRange( mnCurrRefId, 0, 0 ), rRef, bDeleted, bRelativeAsOffset ) : + pushReferenceOperand( rRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::pushBiffReference( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) +{ + return (mnCurrRefId > 0) ? + pushReferenceOperand( getExternalLinks().getSheetRange( mnCurrRefId, 0, 0 ), rRef, bDeleted, bRelativeAsOffset ) : + pushReferenceOperand( rRef, bDeleted, bRelativeAsOffset ); +} + +bool BiffFormulaParserImpl::pushBiffNlrAddr( const BiffNlr& rNlr, bool bRow ) +{ + BinSingleRef2d aRef; + aRef.mnCol = rNlr.mnCol; + aRef.mnRow = rNlr.mnRow; + aRef.mbColRel = !bRow; + aRef.mbRowRel = bRow; + return pushNlrOperand( aRef ); +} + +bool BiffFormulaParserImpl::pushBiffNlrRange( const BiffNlr& rNlr, const BinRange& rRange ) +{ + bool bRow = rNlr.mnRow == rRange.maFirst.mnRow; + return lclIsValidNlrRange( rNlr, rRange, bRow ) ? + pushBiffNlrAddr( rNlr, bRow ) : pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::pushBiffNlrSAddr( const BiffNlr& rNlr, bool bRow ) +{ + BinRange aRange; + aRange.maFirst.mnCol = rNlr.mnCol + (bRow ? 1 : 0); + aRange.maFirst.mnRow = rNlr.mnRow + (bRow ? 0 : 1); + aRange.maLast.mnCol = bRow ? mnMaxApiCol : rNlr.mnCol; + aRange.maLast.mnRow = bRow ? rNlr.mnRow : mnMaxApiRow; + return pushBiffNlrSRange( rNlr, aRange, bRow ); +} + +bool BiffFormulaParserImpl::pushBiffNlrSRange( const BiffNlr& rNlr, const BinRange& rRange, bool bRow ) +{ + if( lclIsValidNlrRange( rNlr, rRange, bRow ) ) + { + BinComplexRef2d aRef; + aRef.maRef1.mnCol = rRange.maFirst.mnCol; + aRef.maRef1.mnRow = rRange.maFirst.mnRow; + aRef.maRef2.mnCol = rRange.maLast.mnCol; + aRef.maRef2.mnRow = rRange.maLast.mnRow; + aRef.maRef1.mbColRel = aRef.maRef2.mbColRel = !bRow && rNlr.mbRel; + aRef.maRef1.mbRowRel = aRef.maRef2.mbRowRel = bRow && rNlr.mbRel; + return pushReferenceOperand( aRef, false, false ); + } + return pushBiffErrorOperand( BIFF_ERR_REF ); +} + +bool BiffFormulaParserImpl::pushBiffName( sal_uInt16 nNameId ) +{ + // one-based in BIFF formulas + return pushDefinedNameOperand( getDefinedNames().getByIndex( static_cast< sal_Int32 >( nNameId ) - 1 ) ); +} + +bool BiffFormulaParserImpl::pushBiffExtName( sal_Int32 nRefId, sal_uInt16 nNameId ) +{ + if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() ) + { + if( pExtLink->getLinkType() == LINKTYPE_SELF ) + return pushBiffName( nNameId ); + // external name indexes are one-based in BIFF + ExternalNameRef xExtName = pExtLink->getNameByIndex( static_cast< sal_Int32 >( nNameId ) - 1 ); + return pushExternalNameOperand( xExtName, pExtLink->getLinkType() ); + } + return pushBiffErrorOperand( BIFF_ERR_NAME ); +} + +bool BiffFormulaParserImpl::pushBiffFunction( sal_uInt16 nFuncId ) +{ + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromBiffFuncId( nFuncId ) ) + if( pFuncInfo->mnMinParamCount == pFuncInfo->mnMaxParamCount ) + return pushFunctionOperator( *pFuncInfo, pFuncInfo->mnMinParamCount ); + return pushFunctionOperator( mrFuncProv.OPCODE_NONAME, 0 ); +} + +bool BiffFormulaParserImpl::pushBiffFunction( sal_uInt16 nFuncId, sal_uInt8 nParamCount ) +{ + if( getFlag( nFuncId, BIFF_TOK_FUNCVAR_CMD ) ) + nParamCount &= BIFF_TOK_FUNCVAR_COUNTMASK; + if( const FunctionInfo* pFuncInfo = mrFuncProv.getFuncInfoFromBiffFuncId( nFuncId ) ) + return pushFunctionOperator( *pFuncInfo, nParamCount ); + return pushFunctionOperator( mrFuncProv.OPCODE_NONAME, nParamCount ); +} + +// ============================================================================ + +FormulaParser::FormulaParser( const WorkbookHelper& rHelper ) : + FormulaProcessorBase( rHelper ) +{ + switch( getFilterType() ) + { + case FILTER_OOX: mxImpl.reset( new OoxFormulaParserImpl( rHelper, maFuncProv ) ); break; + case FILTER_BIFF: mxImpl.reset( new BiffFormulaParserImpl( rHelper, maFuncProv ) ); break; + case FILTER_UNKNOWN: break; + } +} + +FormulaParser::~FormulaParser() +{ +} + +void FormulaParser::importFormula( FormulaContext& rContext, const OUString& rFormulaString ) const +{ + mxImpl->importOoxFormula( rContext, rFormulaString ); +} + +void FormulaParser::importFormula( FormulaContext& rContext, RecordInputStream& rStrm ) const +{ + mxImpl->importOobFormula( rContext, rStrm ); +} + +void FormulaParser::importFormula( FormulaContext& rContext, BiffInputStream& rStrm, const sal_uInt16* pnFmlaSize ) const +{ + mxImpl->importBiffFormula( rContext, rStrm, pnFmlaSize ); +} + +void FormulaParser::convertErrorToFormula( FormulaContext& rContext, sal_uInt8 nErrorCode ) const +{ + ApiTokenSequence aTokens( 3 ); + // HACK: enclose all error codes into an 1x1 matrix + aTokens[ 0 ].OpCode = maFuncProv.OPCODE_ARRAY_OPEN; + aTokens[ 1 ].OpCode = maFuncProv.OPCODE_PUSH; + aTokens[ 1 ].Data <<= BiffHelper::calcDoubleFromError( nErrorCode ); + aTokens[ 2 ].OpCode = maFuncProv.OPCODE_ARRAY_CLOSE; + mxImpl->setFormula( rContext, aTokens ); +} + +void FormulaParser::convertNameToFormula( FormulaContext& rContext, sal_Int32 nTokenIndex ) const +{ + if( nTokenIndex >= 0 ) + { + ApiTokenSequence aTokens( 1 ); + aTokens[ 0 ].OpCode = maFuncProv.OPCODE_NAME; + aTokens[ 0 ].Data <<= nTokenIndex; + mxImpl->setFormula( rContext, aTokens ); + } + else + convertErrorToFormula( rContext, BIFF_ERR_REF ); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/headerfooterparser.cxx b/oox/source/xls/headerfooterparser.cxx new file mode 100644 index 000000000000..663a17c5c89f --- /dev/null +++ b/oox/source/xls/headerfooterparser.cxx @@ -0,0 +1,643 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: headerfooterparser.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/headerfooterparser.hxx" +#include <vector> +#include <set> +#include <rtl/ustrbuf.hxx> +#include <rtl/strbuf.hxx> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/XHeaderFooterContent.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/XTextCursor.hpp> +#include <com/sun/star/text/XTextContent.hpp> +#include <com/sun/star/text/FilenameDisplayFormat.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/xls/stylesbuffer.hxx" +#include "oox/xls/themebuffer.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OString; +using ::rtl::OStringBuffer; +using ::rtl::OUStringToOString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::sheet::XHeaderFooterContent; +using ::com::sun::star::text::XText; +using ::com::sun::star::text::XTextCursor; +using ::com::sun::star::text::XTextContent; +using ::com::sun::star::text::XTextRange; + +namespace oox { +namespace xls { + +// ============================================================================ + +enum HFPortionId +{ + HF_LEFT, + HF_CENTER, + HF_RIGHT, + HF_COUNT +}; + +// ---------------------------------------------------------------------------- + +struct HFPortionInfo +{ + Reference< XText > mxText; /// XText interface of this portion. + Reference< XTextCursor > mxStart; /// Start position of current text range for formatting. + Reference< XTextCursor > mxEnd; /// End position of current text range for formatting. + double mfTotalHeight; /// Sum of heights of previous lines in points. + double mfCurrHeight; /// Height of the current text line in points. + + bool initialize( const Reference< XText >& rxText ); +}; + +bool HFPortionInfo::initialize( const Reference< XText >& rxText ) +{ + mfTotalHeight = mfCurrHeight = 0.0; + mxText = rxText; + if( mxText.is() ) + { + mxStart = mxText->createTextCursor(); + mxEnd = mxText->createTextCursor(); + } + bool bRet = mxText.is() && mxStart.is() && mxEnd.is(); + OSL_ENSURE( bRet, "HFPortionInfo::initialize - missing interfaces" ); + return bRet; +} + +// ---------------------------------------------------------------------------- + +class HeaderFooterParserImpl : public WorkbookHelper +{ +public: + explicit HeaderFooterParserImpl( const WorkbookHelper& rHelper ); + + /** Parses the passed string and creates the header/footer contents. */ + void parse( + const Reference< XHeaderFooterContent >& rxContext, + const OUString& rData ); + + /** Returns the total height of the converted header or footer in points. */ + double getTotalHeight() const; + +private: + /** Returns the current edit engine text object. */ + inline HFPortionInfo& getPortion() { return maPortions[ meCurrPortion ]; } + /** Returns the start cursor of the current text range. */ + inline const Reference< XTextCursor >& getStartPos() { return getPortion().mxStart; } + /** Returns the end cursor of the current text range. */ + inline const Reference< XTextCursor >& getEndPos() { return getPortion().mxEnd; } + + /** Returns the current line height of the specified portion. */ + double getCurrHeight( HFPortionId ePortion ) const; + /** Returns the current line height. */ + double getCurrHeight() const; + + /** Updates the current line height of the specified portion, using the current font size. */ + void updateCurrHeight( HFPortionId ePortion ); + /** Updates the current line height, using the current font size. */ + void updateCurrHeight(); + + /** Sets the font attributes at the current selection. */ + void setAttributes(); + /** Appends and clears internal string buffer. */ + void appendText(); + /** Appends a line break and adjusts internal text height data. */ + void appendLineBreak(); + + /** Creates a text field from the passed service name. */ + Reference< XTextContent > createField( const OUString& rServiceName ) const; + /** Appends the passed text field. */ + void appendField( const Reference< XTextContent >& rxContent ); + + /** Sets the passed font name if it is valid. */ + void convertFontName( const OUString& rStyle ); + /** Converts a font style given as string. */ + void convertFontStyle( const OUString& rStyle ); + /** Converts a font color given as string. */ + void convertFontColor( const OUString& rColor ); + + /** Finalizes current portion: sets font attributes and updates text height data. */ + void finalizePortion(); + /** Changes current header/footer portion. */ + void setNewPortion( HFPortionId ePortion ); + +private: + typedef ::std::vector< HFPortionInfo > HFPortionInfoVec; + typedef ::std::set< OString > OStringSet; + + OUString maPageNumberService; + OUString maPageCountService; + OUString maSheetNameService; + OUString maFileNameService; + OUString maDateTimeService; + OUString maIsDateProp; + OUString maFileFormatProp; + OStringSet maBoldNames; /// All names for bold font style in lowercase UTF-8. + OStringSet maItalicNames; /// All names for italic font style in lowercase UTF-8. + HFPortionInfoVec maPortions; + HFPortionId meCurrPortion; /// Identifier of current H/F portion. + OUStringBuffer maBuffer; /// Text data to append to current text range. + OoxFontData maFontData; /// Font attributes of current text range. +}; + + +HeaderFooterParserImpl::HeaderFooterParserImpl( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maPageNumberService( CREATE_OUSTRING( "com.sun.star.text.TextField.PageNumber" ) ), + maPageCountService( CREATE_OUSTRING( "com.sun.star.text.TextField.PageCount" ) ), + maSheetNameService( CREATE_OUSTRING( "com.sun.star.text.TextField.SheetName" ) ), + maFileNameService( CREATE_OUSTRING( "com.sun.star.text.TextField.FileName" ) ), + maDateTimeService( CREATE_OUSTRING( "com.sun.star.text.TextField.DateTime" ) ), + maIsDateProp( CREATE_OUSTRING( "IsDate" ) ), + maFileFormatProp( CREATE_OUSTRING( "FileFormat" ) ), + maPortions( static_cast< size_t >( HF_COUNT ) ), + meCurrPortion( HF_CENTER ) +{ + // different names for bold font style (lowercase) + maBoldNames.insert( CREATE_OSTRING( "bold" ) ); + maBoldNames.insert( CREATE_OSTRING( "fett" ) ); + maBoldNames.insert( CREATE_OSTRING( "demibold" ) ); + maBoldNames.insert( CREATE_OSTRING( "halbfett" ) ); + maBoldNames.insert( CREATE_OSTRING( "black" ) ); + maBoldNames.insert( CREATE_OSTRING( "heavy" ) ); + + // different names for italic font style (lowercase) + maItalicNames.insert( CREATE_OSTRING( "italic" ) ); + maItalicNames.insert( CREATE_OSTRING( "kursiv" ) ); + maItalicNames.insert( CREATE_OSTRING( "oblique" ) ); + maItalicNames.insert( CREATE_OSTRING( "schr\303\204g" ) ); // with uppercase A umlaut + maItalicNames.insert( CREATE_OSTRING( "schr\303\244g" ) ); // with lowercase A umlaut +} + +void HeaderFooterParserImpl::parse( const Reference< XHeaderFooterContent >& rxContext, const OUString& rData ) +{ + if( !rxContext.is() || (rData.getLength() == 0) || + !maPortions[ HF_LEFT ].initialize( rxContext->getLeftText() ) || + !maPortions[ HF_CENTER ].initialize( rxContext->getCenterText() ) || + !maPortions[ HF_RIGHT ].initialize( rxContext->getRightText() ) ) + return; + + meCurrPortion = HF_CENTER; + maBuffer.setLength( 0 ); + maFontData = getStyles().getDefaultFontData(); + OUStringBuffer aFontName; // current font name + OUStringBuffer aFontStyle; // current font style + sal_Int32 nFontHeight = 0; // current font height + + /** State of the parser. */ + enum + { + STATE_TEXT, /// Literal text data. + STATE_TOKEN, /// Control token following a '&' character. + STATE_FONTNAME, /// Font name ('&' is followed by '"', reads until next '"' or ','). + STATE_FONTSTYLE, /// Font style name (font part after ',', reads until next '"'). + STATE_FONTHEIGHT /// Font height ('&' is followed by num. digits, reads until non-digit). + } + eState = STATE_TEXT; + + const sal_Unicode* pcChar = rData.getStr(); + const sal_Unicode* pcEnd = pcChar + rData.getLength(); + for( ; (pcChar != pcEnd) && (*pcChar != 0); ++pcChar ) + { + sal_Unicode cChar = *pcChar; + switch( eState ) + { + case STATE_TEXT: + { + switch( cChar ) + { + case '&': // new token + appendText(); + eState = STATE_TOKEN; + break; + case '\n': // line break + appendText(); + appendLineBreak(); + break; + default: + maBuffer.append( cChar ); + } + } + break; + + case STATE_TOKEN: + { + // default: back to text mode, may be changed in specific cases + eState = STATE_TEXT; + // ignore case of token codes + if( ('a' <= cChar) && (cChar <= 'z') ) + (cChar -= 'a') += 'A'; + switch( cChar ) + { + case '&': maBuffer.append( cChar ); break; // the '&' character + + case 'L': setNewPortion( HF_LEFT ); break; // left portion + case 'C': setNewPortion( HF_CENTER ); break; // center portion + case 'R': setNewPortion( HF_RIGHT ); break; // right portion + + case 'P': // page number + appendField( createField( maPageNumberService ) ); + break; + case 'N': // total page count + appendField( createField( maPageCountService ) ); + break; + case 'A': // current sheet name + appendField( createField( maSheetNameService ) ); + break; + + case 'F': // file name + { + Reference< XTextContent > xContent = createField( maFileNameService ); + PropertySet aPropSet( xContent ); + aPropSet.setProperty( maFileFormatProp, ::com::sun::star::text::FilenameDisplayFormat::NAME_AND_EXT ); + appendField( xContent ); + } + break; + case 'Z': // file path (without file name), BIFF8 and OOX only + if( (getFilterType() == FILTER_OOX) || ((getFilterType() == FILTER_BIFF) && (getBiff() == BIFF8)) ) + { + Reference< XTextContent > xContent = createField( maFileNameService ); + PropertySet aPropSet( xContent ); + // FilenameDisplayFormat::PATH not supported by Calc + aPropSet.setProperty( maFileFormatProp, ::com::sun::star::text::FilenameDisplayFormat::FULL ); + appendField( xContent ); + /* path only is not supported -- if we find a '&Z&F' + combination for path/name, skip the '&F' part */ + if( (pcChar + 2 < pcEnd) && (pcChar[ 1 ] == '&') && ((pcChar[ 2 ] == 'f') || (pcChar[ 2 ] == 'F')) ) + pcChar += 2; + } + break; + case 'D': // date + { + Reference< XTextContent > xContent = createField( maDateTimeService ); + PropertySet aPropSet( xContent ); + aPropSet.setProperty( maIsDateProp, true ); + appendField( xContent ); + } + break; + case 'T': // time + { + Reference< XTextContent > xContent = createField( maDateTimeService ); + PropertySet aPropSet( xContent ); + aPropSet.setProperty( maIsDateProp, false ); + appendField( xContent ); + } + break; + + case 'B': // bold + setAttributes(); + maFontData.mbBold = !maFontData.mbBold; + break; + case 'I': // italic + setAttributes(); + maFontData.mbItalic = !maFontData.mbItalic; + break; + case 'U': // underline + setAttributes(); + maFontData.mnUnderline = (maFontData.mnUnderline == XML_single) ? XML_none : XML_single; + break; + case 'E': // double underline + setAttributes(); + maFontData.mnUnderline = (maFontData.mnUnderline == XML_double) ? XML_none : XML_double; + break; + case 'S': // strikeout + setAttributes(); + maFontData.mbStrikeout = !maFontData.mbStrikeout; + break; + case 'X': // superscript + setAttributes(); + maFontData.mnEscapement = (maFontData.mnEscapement == XML_superscript) ? XML_baseline : XML_superscript; + break; + case 'Y': // subsrcipt + setAttributes(); + maFontData.mnEscapement = (maFontData.mnEscapement == XML_subscript) ? XML_baseline : XML_subscript; + break; + case 'O': // outlined + setAttributes(); + maFontData.mbOutline = !maFontData.mbOutline; + break; + case 'H': // shadow + setAttributes(); + maFontData.mbShadow = !maFontData.mbShadow; + break; + + case 'K': // text color (not in BIFF) + if( (getFilterType() == FILTER_OOX) && (pcChar + 6 < pcEnd) ) + { + setAttributes(); + // eat the following 6 characters + convertFontColor( OUString( pcChar + 1, 6 ) ); + pcChar += 6; + } + break; + + case '\"': // font name + aFontName.setLength( 0 ); + aFontStyle.setLength( 0 ); + eState = STATE_FONTNAME; + break; + default: + if( ('0' <= cChar) && (cChar <= '9') ) // font size + { + nFontHeight = cChar - '0'; + eState = STATE_FONTHEIGHT; + } + } + } + break; + + case STATE_FONTNAME: + { + switch( cChar ) + { + case '\"': + setAttributes(); + convertFontName( aFontName.makeStringAndClear() ); + eState = STATE_TEXT; + break; + case ',': + eState = STATE_FONTSTYLE; + break; + default: + aFontName.append( cChar ); + } + } + break; + + case STATE_FONTSTYLE: + { + switch( cChar ) + { + case '\"': + setAttributes(); + convertFontName( aFontName.makeStringAndClear() ); + convertFontStyle( aFontStyle.makeStringAndClear() ); + eState = STATE_TEXT; + break; + default: + aFontStyle.append( cChar ); + } + } + break; + + case STATE_FONTHEIGHT: + { + if( ('0' <= cChar) && (cChar <= '9') ) + { + if( nFontHeight >= 0 ) + { + nFontHeight *= 10; + nFontHeight += (cChar - '0'); + if( nFontHeight > 1000 ) + nFontHeight = -1; + } + } + else + { + if( nFontHeight > 0 ) + { + setAttributes(); + maFontData.mfHeight = nFontHeight; + } + --pcChar; + eState = STATE_TEXT; + } + } + break; + } + } + + // finalize + finalizePortion(); + maPortions[ HF_LEFT ].mfTotalHeight += getCurrHeight( HF_LEFT ); + maPortions[ HF_CENTER ].mfTotalHeight += getCurrHeight( HF_CENTER ); + maPortions[ HF_RIGHT ].mfTotalHeight += getCurrHeight( HF_RIGHT ); +} + +double HeaderFooterParserImpl::getTotalHeight() const +{ + return ::std::max( maPortions[ HF_LEFT ].mfTotalHeight, + ::std::max( maPortions[ HF_CENTER ].mfTotalHeight, maPortions[ HF_RIGHT ].mfTotalHeight ) ); +} + +// private -------------------------------------------------------------------- + +double HeaderFooterParserImpl::getCurrHeight( HFPortionId ePortion ) const +{ + double fMaxHt = maPortions[ ePortion ].mfCurrHeight; + return (fMaxHt == 0.0) ? maFontData.mfHeight : fMaxHt; +} + +double HeaderFooterParserImpl::getCurrHeight() const +{ + return getCurrHeight( meCurrPortion ); +} + +void HeaderFooterParserImpl::updateCurrHeight( HFPortionId ePortion ) +{ + double& rfMaxHt = maPortions[ ePortion ].mfCurrHeight; + rfMaxHt = ::std::max( rfMaxHt, maFontData.mfHeight ); +} + +void HeaderFooterParserImpl::updateCurrHeight() +{ + updateCurrHeight( meCurrPortion ); +} + +void HeaderFooterParserImpl::setAttributes() +{ + Reference< XTextRange > xRange( getStartPos(), UNO_QUERY ); + getEndPos()->gotoRange( xRange, sal_False ); + getEndPos()->gotoEnd( sal_True ); + if( !getEndPos()->isCollapsed() ) + { + Font aFont( *this, maFontData ); + aFont.finalizeImport(); + PropertySet aPropSet( getEndPos() ); + aFont.writeToPropertySet( aPropSet, FONT_PROPTYPE_RICHTEXT ); + getStartPos()->gotoEnd( sal_False ); + getEndPos()->gotoEnd( sal_False ); + } +} + +void HeaderFooterParserImpl::appendText() +{ + if( maBuffer.getLength() > 0 ) + { + getEndPos()->gotoEnd( sal_False ); + getEndPos()->setString( maBuffer.makeStringAndClear() ); + updateCurrHeight(); + } +} + +void HeaderFooterParserImpl::appendLineBreak() +{ + getEndPos()->gotoEnd( sal_False ); + getEndPos()->setString( OUString( sal_Unicode( '\n' ) ) ); + getPortion().mfTotalHeight += getCurrHeight(); + getPortion().mfCurrHeight = 0; +} + +Reference< XTextContent > HeaderFooterParserImpl::createField( const OUString& rServiceName ) const +{ + Reference< XTextContent > xContent; + try + { + Reference< XMultiServiceFactory > xFactory( getDocument(), UNO_QUERY_THROW ); + xContent.set( xFactory->createInstance( rServiceName ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + OSL_ENSURE( false, + OStringBuffer( "HeaderFooterParserImpl::createField - error while creating text field \"" ). + append( OUStringToOString( rServiceName, RTL_TEXTENCODING_ASCII_US ) ). + append( '"' ).getStr() ); + } + return xContent; +} + +void HeaderFooterParserImpl::appendField( const Reference< XTextContent >& rxContent ) +{ + getEndPos()->gotoEnd( sal_False ); + try + { + Reference< XTextRange > xRange( getEndPos(), UNO_QUERY_THROW ); + getPortion().mxText->insertTextContent( xRange, rxContent, sal_False ); + updateCurrHeight(); + } + catch( Exception& ) + { + } +} + +void HeaderFooterParserImpl::convertFontName( const OUString& rName ) +{ + if( rName.getLength() > 0 ) + { + // single dash is document default font + if( (rName.getLength() == 1) && (rName[ 0 ] == '-') ) + maFontData.maName = getStyles().getDefaultFontData().maName; + else + maFontData.maName = rName; + } +} + +void HeaderFooterParserImpl::convertFontStyle( const OUString& rStyle ) +{ + maFontData.mbBold = maFontData.mbItalic = false; + sal_Int32 nPos = 0; + sal_Int32 nLen = rStyle.getLength(); + while( (0 <= nPos) && (nPos < nLen) ) + { + OString aToken = OUStringToOString( rStyle.getToken( 0, ' ', nPos ), RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase(); + if( aToken.getLength() > 0 ) + { + if( maBoldNames.count( aToken ) > 0 ) + maFontData.mbBold = true; + else if( maItalicNames.count( aToken ) > 0 ) + maFontData.mbItalic = true; + } + } +} + +void HeaderFooterParserImpl::convertFontColor( const OUString& rColor ) +{ + OSL_ENSURE( rColor.getLength() == 6, "HeaderFooterParserImpl::convertFontColor - invalid font color code" ); + if( (rColor[ 2 ] == '+') || (rColor[ 2 ] == '-') ) + // theme color: TTSNNN (TT = decimal theme index, S = +/-, NNN = decimal tint/shade in percent) + maFontData.maColor.set( + XML_theme, rColor.copy( 0, 2 ).toInt32(), + static_cast< double >( rColor.copy( 2 ).toInt32() ) / 100.0 ); + else + // RGB color: RRGGBB + maFontData.maColor.set( XML_rgb, rColor.toInt32( 16 ) ); +} + +void HeaderFooterParserImpl::finalizePortion() +{ + appendText(); + setAttributes(); +} + +void HeaderFooterParserImpl::setNewPortion( HFPortionId ePortion ) +{ + if( ePortion != meCurrPortion ) + { + finalizePortion(); + meCurrPortion = ePortion; + maFontData = getStyles().getDefaultFontData(); + } +} + +// ============================================================================ + +HeaderFooterParser::HeaderFooterParser( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mxImpl( new HeaderFooterParserImpl( rHelper ) ) +{ +} + +HeaderFooterParser::~HeaderFooterParser() +{ +} + +void HeaderFooterParser::parse( const Reference< XHeaderFooterContent >& rxContext, const OUString& rData ) +{ + mxImpl->parse( rxContext, rData ); +} + +double HeaderFooterParser::getTotalHeight() const +{ + return mxImpl->getTotalHeight(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/makefile.mk b/oox/source/xls/makefile.mk new file mode 100644 index 000000000000..f46737cb307b --- /dev/null +++ b/oox/source/xls/makefile.mk @@ -0,0 +1,106 @@ +#************************************************************************* +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.2 $ +# +# last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ +# +# The Contents of this file are made available subject to +# the terms of GNU Lesser General Public License Version 2.1. +# +# +# GNU Lesser General Public License Version 2.1 +# ============================================= +# Copyright 2005 by Sun Microsystems, Inc. +# 901 San Antonio Road, Palo Alto, CA 94303, USA +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License version 2.1, as published by the Free Software Foundation. +# +# This library 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 for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=oox +TARGET=xls +AUTOSEG=true + +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE: $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES = \ + $(SLO)$/addressconverter.obj \ + $(SLO)$/autofiltercontext.obj \ + $(SLO)$/biffcodec.obj \ + $(SLO)$/biffdetector.obj \ + $(SLO)$/bifffragmenthandler.obj \ + $(SLO)$/biffhelper.obj \ + $(SLO)$/biffinputstream.obj \ + $(SLO)$/biffoutputstream.obj \ + $(SLO)$/condformatbuffer.obj \ + $(SLO)$/condformatcontext.obj \ + $(SLO)$/connectionsfragment.obj \ + $(SLO)$/defnamesbuffer.obj \ + $(SLO)$/excelfilter.obj \ + $(SLO)$/externallinkbuffer.obj \ + $(SLO)$/externallinkfragment.obj \ + $(SLO)$/formulabase.obj \ + $(SLO)$/formulaparser.obj \ + $(SLO)$/headerfooterparser.obj \ + $(SLO)$/numberformatsbuffer.obj \ + $(SLO)$/ooxcontexthandler.obj \ + $(SLO)$/ooxcontexthelper.obj \ + $(SLO)$/ooxfragmenthandler.obj \ + $(SLO)$/pagesettings.obj \ + $(SLO)$/pivotcachefragment.obj \ + $(SLO)$/pivottablebuffer.obj \ + $(SLO)$/pivottablefragment.obj \ + $(SLO)$/querytablefragment.obj \ + $(SLO)$/richstring.obj \ + $(SLO)$/richstringcontext.obj \ + $(SLO)$/sharedformulabuffer.obj \ + $(SLO)$/sharedstringsbuffer.obj \ + $(SLO)$/sharedstringsfragment.obj \ + $(SLO)$/sheetcellrangemap.obj \ + $(SLO)$/sheetdatacontext.obj \ + $(SLO)$/stylesbuffer.obj \ + $(SLO)$/stylesfragment.obj \ + $(SLO)$/stylespropertyhelper.obj \ + $(SLO)$/tablebuffer.obj \ + $(SLO)$/tablefragment.obj \ + $(SLO)$/themebuffer.obj \ + $(SLO)$/unitconverter.obj \ + $(SLO)$/validationpropertyhelper.obj \ + $(SLO)$/viewsettings.obj \ + $(SLO)$/webquerybuffer.obj \ + $(SLO)$/workbookfragment.obj \ + $(SLO)$/workbookhelper.obj \ + $(SLO)$/workbooksettings.obj \ + $(SLO)$/worksheetbuffer.obj \ + $(SLO)$/worksheetfragment.obj \ + $(SLO)$/worksheethelper.obj \ + $(SLO)$/worksheetsettings.obj + +# --- Targets ------------------------------------------------------- + +.INCLUDE : target.mk diff --git a/oox/source/xls/numberformatsbuffer.cxx b/oox/source/xls/numberformatsbuffer.cxx new file mode 100644 index 000000000000..3433a7885704 --- /dev/null +++ b/oox/source/xls/numberformatsbuffer.cxx @@ -0,0 +1,2124 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: numberformatsbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/numberformatsbuffer.hxx" +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/XNumberFormats.hpp> +#include <com/sun/star/util/XNumberFormatTypes.hpp> +#include <com/sun/star/i18n/NumberFormatIndex.hpp> +#include <osl/thread.h> +#include <rtl/string.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <comphelper/processfactory.hxx> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" + +using ::rtl::OString; +using ::rtl::OStringBuffer; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OStringToOUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::util::XNumberFormatsSupplier; +using ::com::sun::star::util::XNumberFormats; +using ::com::sun::star::util::XNumberFormatTypes; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +/** Stores the number format used in Calc for an Excel built-in number format. */ +struct BuiltinFormat +{ + sal_Int32 mnNumFmtId; /// Built-in number format index. + const sal_Char* mpcFmtCode; /// Format string, UTF-8, may be 0 (mnPredefId is used then). + sal_Int16 mnPredefId; /// Predefined format index, if mpcFmtCode is 0. + sal_Int32 mnReuseId; /// Use this format, if mpcFmtCode is 0 and mnPredefId is -1. +}; + +/** Defines a literal built-in number format. */ +#define NUMFMT_STRING( INDEX, FORMATCODE ) \ + { INDEX, FORMATCODE, -1, -1 } + +/** Defines a built-in number format that maps to an own predefined format. */ +#define NUMFMT_PREDEF( INDEX, PREDEFINED ) \ + { INDEX, 0, ::com::sun::star::i18n::NumberFormatIndex::PREDEFINED, -1 } + +/** Defines a built-in number format that is the same as the specified in nReuseId. */ +#define NUMFMT_REUSE( INDEX, REUSED_INDEX ) \ + { INDEX, 0, -1, REUSED_INDEX } + +/** Terminates a built-in number format table. */ +#define NUMFMT_ENDTABLE() \ + { -1, 0, -1, -1 } + +/** Defines builtin date and time formats 14...22. + @param SYSTEMDATE Complete short system date (for formats 14 and 22). + @param DAY Day format (for formats 15 and 16). + @param DAYSEP Separator between day and month (for formats 15 and 16). + @param MONTH Month format (for formats 15...17). + @param MONTHSEP Separator between month and year (for formats 15 and 17). + @param YEAR Year format (for formats 15 and 17). + @param HOUR12 Hour format for 12-hour AM/PM formats (formats 18 and 19). + @param HOUR24 Hour format for 24-hour formats (formats 20...22). */ +#define NUMFMT_ALLDATETIMES( SYSTEMDATE, DAY, DAYSEP, MONTH, MONTHSEP, YEAR, HOUR12, HOUR24 ) \ + NUMFMT_STRING( 14, SYSTEMDATE ), \ + NUMFMT_STRING( 15, DAY DAYSEP MONTH MONTHSEP YEAR ), \ + NUMFMT_STRING( 16, DAY DAYSEP MONTH ), \ + NUMFMT_STRING( 17, MONTH MONTHSEP YEAR ), \ + NUMFMT_STRING( 18, HOUR12 ":mm AM/PM" ), \ + NUMFMT_STRING( 19, HOUR12 ":mm:ss AM/PM" ), \ + NUMFMT_STRING( 20, HOUR24 ":mm" ), \ + NUMFMT_STRING( 21, HOUR24 ":mm:ss" ), \ + NUMFMT_STRING( 22, SYSTEMDATE " " HOUR24 ":mm" ) + +/** Defines builtin time formats INDEX and INDEX+1 for CJK locales. + @param INDEX First number format index. + @param HOURFORMAT Hour format. + @param HOUR Hour symbol. + @param MINUTE Minute symbol. + @param SECOND Second symbol. */ +#define NUMFMT_TIME_CJK( INDEX, HOURFORMAT, HOUR, MINUTE, SECOND ) \ + NUMFMT_STRING( INDEX + 0, HOURFORMAT "\"" HOUR "\"mm\"" MINUTE "\"" ), \ + NUMFMT_STRING( INDEX + 1, HOURFORMAT "\"" HOUR "\"mm\"" MINUTE "\"ss\"" SECOND "\"" ) + +/** Defines builtin time formats 32...35 for CJK locales. + @param HOUR12 Hour format for 12-hour AM/PM formats (formats 34 and 35). + @param HOUR24 Hour format for 24-hour formats (formats 32 and 33). + @param HOUR Hour symbol. + @param MINUTE Minute symbol. + @param SECOND Second symbol. */ +#define NUMFMT_ALLTIMES_CJK( HOUR12, HOUR24, HOUR, MINUTE, SECOND ) \ + NUMFMT_TIME_CJK( 32, HOUR24, HOUR, MINUTE, SECOND ), \ + NUMFMT_TIME_CJK( 34, "AM/PM" HOUR12, HOUR, MINUTE, SECOND ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "symbol, [minus], number". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0;" MODIF SYMBOL SPACE "-#,##0" ), \ + NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0;" "[RED]" MODIF SYMBOL SPACE "-#,##0" ), \ + NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00;" MODIF SYMBOL SPACE "-#,##0.00" ), \ + NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00;" "[RED]" MODIF SYMBOL SPACE "-#,##0.00" ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following format: + "symbol, [minus], number". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ACCOUNTING_SYMBOL_MINUS_NUMBER( INDEX, SYMBOL, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_ " "* #,##0_ ;" "_ " "* -#,##0_ ;" "_ " "* \"-\"_ ;" "_ @_ " ), \ + NUMFMT_STRING( INDEX + 1, "_ " SYMBOL SPACE "* #,##0_ ;" "_ " SYMBOL SPACE "* -#,##0_ ;" "_ " SYMBOL SPACE "* \"-\"_ ;" "_ @_ " ), \ + NUMFMT_STRING( INDEX + 2, "_ " "* #,##0.00_ ;" "_ " "* -#,##0.00_ ;" "_ " "* \"-\"?\?_ ;" "_ @_ " ), \ + NUMFMT_STRING( INDEX + 3, "_ " SYMBOL SPACE "* #,##0.00_ ;" "_ " SYMBOL SPACE "* -#,##0.00_ ;" "_ " SYMBOL SPACE "* \"-\"?\?_ ;" "_ @_ " ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following format: + "symbol, [minus], number". + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( SYMBOL, SPACE ) \ + NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( 37, "", "", "" ), \ + NUMFMT_ACCOUNTING_SYMBOL_MINUS_NUMBER( 41, SYMBOL, SPACE ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "symbol, number, [minus]". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0_-;" MODIF SYMBOL SPACE "#,##0-" ), \ + NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0_-;" "[RED]" MODIF SYMBOL SPACE "#,##0-" ), \ + NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00_-;" MODIF SYMBOL SPACE "#,##0.00-" ), \ + NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00_-;" "[RED]" MODIF SYMBOL SPACE "#,##0.00-" ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following format: + "symbol, number, [minus]". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ACCOUNTING_SYMBOL_NUMBER_MINUS( INDEX, SYMBOL, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_-" "* #,##0_-;" "_-" "* #,##0-;" "_-" "* \"-\"_-;" "_-@_-" ), \ + NUMFMT_STRING( INDEX + 1, "_-" SYMBOL SPACE "* #,##0_-;" "_-" SYMBOL SPACE "* #,##0-;" "_-" SYMBOL SPACE "* \"-\"_-;" "_-@_-" ), \ + NUMFMT_STRING( INDEX + 2, "_-" "* #,##0.00_-;" "_-" "* #,##0.00-;" "_-" "* \"-\"?\?_-;" "_-@_-" ), \ + NUMFMT_STRING( INDEX + 3, "_-" SYMBOL SPACE "* #,##0.00_-;" "_-" SYMBOL SPACE "* #,##0.00-;" "_-" SYMBOL SPACE "* \"-\"?\?_-;" "_-@_-" ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following format: + "symbol, number, [minus]". + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( SYMBOL, SPACE ) \ + NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( 37, "", "", "" ), \ + NUMFMT_ACCOUNTING_SYMBOL_NUMBER_MINUS( 41, SYMBOL, SPACE ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "number, symbol, [minus]". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between number and currency symbol. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL "_-;" MODIF "#,##0" SPACE SYMBOL "-" ), \ + NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL "_-;" "[RED]" MODIF "#,##0" SPACE SYMBOL "-" ), \ + NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL "_-;" MODIF "#,##0.00" SPACE SYMBOL "-" ), \ + NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL "_-;" "[RED]" MODIF "#,##0.00" SPACE SYMBOL "-" ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following format: + "number, symbol, [minus]". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param BLINDS Blind currency symbol. + @param SPACE Space character(s) between number and currency symbol. */ +#define NUMFMT_ACCOUNTING_NUMBER_SYMBOL_MINUS( INDEX, SYMBOL, BLINDS, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_-* #,##0" SPACE BLINDS "_-;_-* #,##0" SPACE BLINDS "-;_-* \"-\"" SPACE BLINDS "_-;_-@_-" ), \ + NUMFMT_STRING( INDEX + 1, "_-* #,##0" SPACE SYMBOL "_-;_-* #,##0" SPACE SYMBOL "-;_-* \"-\"" SPACE SYMBOL "_-;_-@_-" ), \ + NUMFMT_STRING( INDEX + 2, "_-* #,##0.00" SPACE BLINDS "_-;_-* #,##0.00" SPACE BLINDS "-;_-* \"-\"?\?" SPACE BLINDS "_-;_-@_-" ), \ + NUMFMT_STRING( INDEX + 3, "_-* #,##0.00" SPACE SYMBOL "_-;_-* #,##0.00" SPACE SYMBOL "-;_-* \"-\"?\?" SPACE SYMBOL "_-;_-@_-" ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following format: + "number, symbol, [minus]". + @param SYMBOL Currency symbol. + @param BLINDS Blind currency symbol. + @param SPACE Space character(s) between number and currency symbol. */ +#define NUMFMT_ALLCURRENCIES_NUMBER_SYMBOL_MINUS( SYMBOL, BLINDS, SPACE ) \ + NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( 37, BLINDS, SPACE, "" ), \ + NUMFMT_ACCOUNTING_NUMBER_SYMBOL_MINUS( 41, SYMBOL, BLINDS, SPACE ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "[minus], symbol, number". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0;" MODIF "-" SYMBOL SPACE "#,##0" ), \ + NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0;" "[RED]" MODIF "-" SYMBOL SPACE "#,##0" ), \ + NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00;" MODIF "-" SYMBOL SPACE "#,##0.00" ), \ + NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00;" "[RED]" MODIF "-" SYMBOL SPACE "#,##0.00" ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following order: + "[minus], symbol, number". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( INDEX, SYMBOL, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_-" "* #,##0_-;" "-" "* #,##0_-;" "_-" "* \"-\"_-;" "_-@_-" ), \ + NUMFMT_STRING( INDEX + 1, "_-" SYMBOL SPACE "* #,##0_-;" "-" SYMBOL SPACE "* #,##0_-;" "_-" SYMBOL SPACE "* \"-\"_-;" "_-@_-" ), \ + NUMFMT_STRING( INDEX + 2, "_-" "* #,##0.00_-;" "-" "* #,##0.00_-;" "_-" "* \"-\"?\?_-;" "_-@_-" ), \ + NUMFMT_STRING( INDEX + 3, "_-" SYMBOL SPACE "* #,##0.00_-;" "-" SYMBOL SPACE "* #,##0.00_-;" "_-" SYMBOL SPACE "* \"-\"?\?_-;" "_-@_-" ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following order: + "[minus], symbol, number". + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( SYMBOL, SPACE ) \ + NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 37, "", "", "" ), \ + NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( 41, SYMBOL, SPACE ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "[minus], number, symbol". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between number and currency symbol. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL ";" MODIF "-#,##0" SPACE SYMBOL ), \ + NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL ";" "[RED]" MODIF "-#,##0" SPACE SYMBOL ), \ + NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL ";" MODIF "-#,##0.00" SPACE SYMBOL ), \ + NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL ";" "[RED]" MODIF "-#,##0.00" SPACE SYMBOL ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following format: + "[minus], number, symbol". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param BLINDS Blind currency symbol. + @param SPACE Space character(s) between number and currency symbol. */ +#define NUMFMT_ACCOUNTING_MINUS_NUMBER_SYMBOL( INDEX, SYMBOL, BLINDS, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_-* #,##0" SPACE BLINDS "_-;-* #,##0" SPACE BLINDS "_-;_-* \"-\"" SPACE BLINDS "_-;_-@_-" ), \ + NUMFMT_STRING( INDEX + 1, "_-* #,##0" SPACE SYMBOL "_-;-* #,##0" SPACE SYMBOL "_-;_-* \"-\"" SPACE SYMBOL "_-;_-@_-" ), \ + NUMFMT_STRING( INDEX + 2, "_-* #,##0.00" SPACE BLINDS "_-;-* #,##0.00" SPACE BLINDS "_-;_-* \"-\"?\?" SPACE BLINDS "_-;_-@_-" ), \ + NUMFMT_STRING( INDEX + 3, "_-* #,##0.00" SPACE SYMBOL "_-;-* #,##0.00" SPACE SYMBOL "_-;_-* \"-\"?\?" SPACE SYMBOL "_-;_-@_-" ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following format: + "[minus], number, symbol". + @param SYMBOL Currency symbol. + @param BLINDS Blind currency symbol. + @param SPACE Space character(s) between number and currency symbol. */ +#define NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( SYMBOL, BLINDS, SPACE ) \ + NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( 37, BLINDS, SPACE, "" ), \ + NUMFMT_ACCOUNTING_MINUS_NUMBER_SYMBOL( 41, SYMBOL, BLINDS, SPACE ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "[opening parenthesis], symbol, number, [closing parenthesis].". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0_);" MODIF "(" SYMBOL SPACE "#,##0)" ), \ + NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0_);" "[RED]" MODIF "(" SYMBOL SPACE "#,##0)" ), \ + NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00_);" MODIF "(" SYMBOL SPACE "#,##0.00)" ), \ + NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00_);" "[RED]" MODIF "(" SYMBOL SPACE "#,##0.00)" ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following format: + "[opening parenthesis], symbol, number, [closing parenthesis].". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ACCOUNTING_OPEN_SYMBOL_NUMBER_CLOSE( INDEX, SYMBOL, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_(" "* #,##0_);" "_(" "* (#,##0);" "_(" "* \"-\"_);" "_(@_)" ), \ + NUMFMT_STRING( INDEX + 1, "_(" SYMBOL SPACE "* #,##0_);" "_(" SYMBOL SPACE "* (#,##0);" "_(" SYMBOL SPACE "* \"-\"_);" "_(@_)" ), \ + NUMFMT_STRING( INDEX + 2, "_(" "* #,##0.00_);" "_(" "* (#,##0.00);" "_(" "* \"-\"?\?_);" "_(@_)" ), \ + NUMFMT_STRING( INDEX + 3, "_(" SYMBOL SPACE "* #,##0.00_);" "_(" SYMBOL SPACE "* (#,##0.00);" "_(" SYMBOL SPACE "* \"-\"?\?_);" "_(@_)" ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following format: + "[opening parenthesis], symbol, number, [closing parenthesis].". + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between currency symbol and number. */ +#define NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( SYMBOL, SPACE ) \ + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 37, "", "", "" ), \ + NUMFMT_ACCOUNTING_OPEN_SYMBOL_NUMBER_CLOSE( 41, SYMBOL, SPACE ) + +/** Defines builtin currency formats INDEX...INDEX+3 in the following format: + "[opening parenthesis], number, symbol, [closing parenthesis].". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param SPACE Space character(s) between number and currency symbol. + @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */ +#define NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( INDEX, SYMBOL, SPACE, MODIF ) \ + NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL "_);" MODIF "(#,##0" SPACE SYMBOL ")" ), \ + NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL "_);" "[RED]" MODIF "(#,##0" SPACE SYMBOL ")" ), \ + NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL "_);" MODIF "(#,##0.00" SPACE SYMBOL ")" ), \ + NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL "_);" "[RED]" MODIF "(#,##0.00" SPACE SYMBOL ")" ) + +/** Defines builtin accounting formats INDEX...INDEX+3 in the following format: + "[opening parenthesis], number, symbol, [closing parenthesis].". + @param INDEX First number format index. + @param SYMBOL Currency symbol. + @param BLINDS Blind currency symbol. + @param SPACE Space character(s) between number and currency symbol. */ +#define NUMFMT_ACCOUNTING_OPEN_NUMBER_SYMBOL_CLOSE( INDEX, SYMBOL, BLINDS, SPACE ) \ + NUMFMT_STRING( INDEX + 0, "_ * #,##0_)" SPACE BLINDS "_ ;_ * (#,##0)" SPACE BLINDS "_ ;_ * \"-\"_)" SPACE BLINDS "_ ;_ @_ " ), \ + NUMFMT_STRING( INDEX + 1, "_ * #,##0_)" SPACE SYMBOL "_ ;_ * (#,##0)" SPACE SYMBOL "_ ;_ * \"-\"_)" SPACE SYMBOL "_ ;_ @_ " ), \ + NUMFMT_STRING( INDEX + 2, "_ * #,##0.00_)" SPACE BLINDS "_ ;_ * (#,##0.00)" SPACE BLINDS "_ ;_ * \"-\"?\?_)" SPACE BLINDS "_ ;_ @_ " ), \ + NUMFMT_STRING( INDEX + 3, "_ * #,##0.00_)" SPACE SYMBOL "_ ;_ * (#,##0.00)" SPACE SYMBOL "_ ;_ * \"-\"?\?_)" SPACE SYMBOL "_ ;_ @_ " ) + +/** Defines builtin currency formats 5...8 (with currency symbol), 37...40 + (blind currency symbol), and 41...44 (accounting), in the following format: + "[opening parenthesis], number, symbol, [closing parenthesis].". + @param SYMBOL Currency symbol. + @param BLINDS Blind currency symbol. + @param SPACE Space character(s) between number and currency symbol. */ +#define NUMFMT_ALLCURRENCIES_OPEN_NUMBER_SYMBOL_CLOSE( SYMBOL, BLINDS, SPACE ) \ + NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( 5, SYMBOL, SPACE, "" ), \ + NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( 37, BLINDS, SPACE, "" ), \ + NUMFMT_ACCOUNTING_OPEN_NUMBER_SYMBOL_CLOSE( 41, SYMBOL, BLINDS, SPACE ) + +// currency unit characters +#define UTF8_BAHT "\340\270\277" +#define UTF8_COLON "\342\202\241" +#define UTF8_CURR_AR_AE "\330\257.\330\245." +#define UTF8_CURR_AR_BH "\330\257.\330\250." +#define UTF8_CURR_AR_DZ "\330\257.\330\254." +#define UTF8_CURR_AR_EG "\330\254.\331\205." +#define UTF8_CURR_AR_IQ "\330\257.\330\271." +#define UTF8_CURR_AR_JO "\330\257.\330\247." +#define UTF8_CURR_AR_KW "\330\257.\331\203." +#define UTF8_CURR_AR_LB "\331\204.\331\204." +#define UTF8_CURR_AR_LY "\330\257.\331\204." +#define UTF8_CURR_AR_MA "\330\257.\331\205." +#define UTF8_CURR_AR_OM "\330\261.\330\271." +#define UTF8_CURR_AR_QA "\330\261.\331\202." +#define UTF8_CURR_AR_SA "\330\261.\330\263." +#define UTF8_CURR_AR_SY "\331\204.\330\263." +#define UTF8_CURR_AR_TN "\330\257.\330\252." +#define UTF8_CURR_AR_YE "\330\261.\331\212." +#define UTF8_CURR_BN_IN "\340\246\237\340\246\276" +#define UTF8_CURR_FA_IR "\330\261\331\212\330\247\331\204" +#define UTF8_CURR_GU_IN "\340\252\260\340\253\202" +#define UTF8_CURR_HI_IN "\340\244\260\340\245\201" +#define UTF8_CURR_KN_IN "\340\262\260\340\263\202" +#define UTF8_CURR_ML_IN "\340\264\225" +#define UTF8_CURR_PA_IN "\340\250\260\340\251\201" +#define UTF8_CURR_TA_IN "\340\256\260\340\257\202" +#define UTF8_CURR_TE_IN "\340\260\260\340\261\202" +#define UTF8_DONG "\342\202\253" +#define UTF8_EURO "\342\202\254" +#define UTF8_POUND_GB "\302\243" +#define UTF8_RUFIYAA "\336\203" +#define UTF8_SHEQEL "\342\202\252" +#define UTF8_TUGRUG "\342\202\256" +#define UTF8_WON "\342\202\251" +#define UTF8_YEN_CN "\357\277\245" +#define UTF8_YEN_JP "\302\245" + +// Unicode characters for currency units +#define UTF8_CCARON_LC "\304\215" +#define UTF8_LSTROKE_LC "\305\202" +// Armenian +#define UTF8_HY_DA_LC "\325\244" +#define UTF8_HY_REH_LC "\326\200" +// Cyrillic +#define UTF8_CYR_G_LC "\320\263" +#define UTF8_CYR_L_LC "\320\273" +#define UTF8_CYR_M_LC "\320\274" +#define UTF8_CYR_N_LC "\320\275" +#define UTF8_CYR_O_LC "\320\276" +#define UTF8_CYR_R_LC "\321\200" +#define UTF8_CYR_S_LC "\321\201" +#define UTF8_CYR_W_LC "\320\262" + +// Japanese/Chinese date/time characters +#define UTF8_CJ_YEAR "\345\271\264" +#define UTF8_CJ_MON "\346\234\210" +#define UTF8_CJ_DAY "\346\227\245" +#define UTF8_CJ_HOUR "\346\231\202" +#define UTF8_CJ_MIN "\345\210\206" +#define UTF8_CJ_SEC "\347\247\222" + +// Chinese Simplified date/time characters +#define UTF8_CS_YEAR "\345\271\264" +#define UTF8_CS_MON "\346\234\210" +#define UTF8_CS_DAY "\346\227\245" +#define UTF8_CS_HOUR "\346\227\266" +#define UTF8_CS_MIN "\345\210\206" +#define UTF8_CS_SEC "\347\247\222" + +// Korean date/time characters +#define UTF8_KO_YEAR "\353\205\204" +#define UTF8_KO_MON "\354\233\224" +#define UTF8_KO_DAY "\354\235\274" +#define UTF8_KO_HOUR "\354\213\234" +#define UTF8_KO_MIN "\353\266\204" +#define UTF8_KO_SEC "\354\264\210" + +// ---------------------------------------------------------------------------- + +/** Default number format table. Last parent of all other tables, used for unknown locales. */ +static const BuiltinFormat spBuiltinFormats_BASE[] = +{ + // 0..13 numeric and currency formats + NUMFMT_PREDEF( 0, NUMBER_STANDARD ), // General + NUMFMT_PREDEF( 1, NUMBER_INT ), // 0 + NUMFMT_PREDEF( 2, NUMBER_DEC2 ), // 0.00 + NUMFMT_PREDEF( 3, NUMBER_1000INT ), // #,##0 + NUMFMT_PREDEF( 4, NUMBER_1000DEC2 ), // #,##0.00 + NUMFMT_PREDEF( 5, CURRENCY_1000INT ), // #,##0[symbol] + NUMFMT_PREDEF( 6, CURRENCY_1000INT_RED ), // #,##0[symbol];[RED]-#,##0[symbol] + NUMFMT_PREDEF( 7, CURRENCY_1000DEC2 ), // #,##0.00[symbol] + NUMFMT_PREDEF( 8, CURRENCY_1000DEC2_RED ), // #,##0.00[symbol];[RED]-#,##0.00[symbol] + NUMFMT_PREDEF( 9, PERCENT_INT ), // 0% + NUMFMT_PREDEF( 10, PERCENT_DEC2 ), // 0.00% + NUMFMT_PREDEF( 11, SCIENTIFIC_000E00 ), // 0.00E+00 + NUMFMT_PREDEF( 12, FRACTION_1 ), // # ?/? + NUMFMT_PREDEF( 13, FRACTION_2 ), // # ??/?? + + // 14...22 date and time formats + NUMFMT_PREDEF( 14, DATE_SYS_DDMMYYYY ), + NUMFMT_PREDEF( 15, DATE_SYS_DMMMYY ), + NUMFMT_PREDEF( 16, DATE_SYS_DDMMM ), + NUMFMT_PREDEF( 17, DATE_SYS_MMYY ), + NUMFMT_PREDEF( 18, TIME_HHMMAMPM ), + NUMFMT_PREDEF( 19, TIME_HHMMSSAMPM ), + NUMFMT_PREDEF( 20, TIME_HHMM ), + NUMFMT_PREDEF( 21, TIME_HHMMSS ), + NUMFMT_PREDEF( 22, DATETIME_SYSTEM_SHORT_HHMM ), + + // 23...36 international formats + NUMFMT_REUSE( 23, 0 ), + NUMFMT_REUSE( 24, 0 ), + NUMFMT_REUSE( 25, 0 ), + NUMFMT_REUSE( 26, 0 ), + NUMFMT_REUSE( 27, 14 ), + NUMFMT_REUSE( 28, 14 ), + NUMFMT_REUSE( 29, 14 ), + NUMFMT_REUSE( 30, 14 ), + NUMFMT_REUSE( 31, 14 ), + NUMFMT_REUSE( 32, 21 ), + NUMFMT_REUSE( 33, 21 ), + NUMFMT_REUSE( 34, 21 ), + NUMFMT_REUSE( 35, 21 ), + NUMFMT_REUSE( 36, 14 ), + + // 37...44 accounting formats, defaults without currency symbol here + NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 37, "", "", "" ), + NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( 41, "", "" ), + + // 45...49 more special formats + NUMFMT_STRING( 45, "mm:ss" ), + NUMFMT_STRING( 46, "[h]:mm:ss" ), + NUMFMT_STRING( 47, "mm:ss.0" ), + NUMFMT_STRING( 48, "##0.0E+0" ), + NUMFMT_PREDEF( 49, TEXT ), + + // 50...81 international formats + NUMFMT_REUSE( 50, 14 ), + NUMFMT_REUSE( 51, 14 ), + NUMFMT_REUSE( 52, 14 ), + NUMFMT_REUSE( 53, 14 ), + NUMFMT_REUSE( 54, 14 ), + NUMFMT_REUSE( 55, 14 ), + NUMFMT_REUSE( 56, 14 ), + NUMFMT_REUSE( 57, 14 ), + NUMFMT_REUSE( 58, 14 ), + NUMFMT_REUSE( 59, 1 ), + NUMFMT_REUSE( 60, 2 ), + NUMFMT_REUSE( 61, 3 ), + NUMFMT_REUSE( 62, 4 ), + NUMFMT_REUSE( 63, 5 ), + NUMFMT_REUSE( 64, 6 ), + NUMFMT_REUSE( 65, 7 ), + NUMFMT_REUSE( 66, 8 ), + NUMFMT_REUSE( 67, 9 ), + NUMFMT_REUSE( 68, 10 ), + NUMFMT_REUSE( 69, 12 ), + NUMFMT_REUSE( 70, 13 ), + NUMFMT_REUSE( 71, 14 ), + NUMFMT_REUSE( 72, 14 ), + NUMFMT_REUSE( 73, 15 ), + NUMFMT_REUSE( 74, 16 ), + NUMFMT_REUSE( 75, 17 ), + NUMFMT_REUSE( 76, 20 ), + NUMFMT_REUSE( 77, 21 ), + NUMFMT_REUSE( 78, 22 ), + NUMFMT_REUSE( 79, 45 ), + NUMFMT_REUSE( 80, 46 ), + NUMFMT_REUSE( 81, 47 ), + + // 82...163 not used, must not occur in a file (Excel may crash) + + NUMFMT_ENDTABLE() +}; + +// ---------------------------------------------------------------------------- + +/** Arabic, U.A.E. */ +static const BuiltinFormat spBuiltinFormats_ar_AE[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_AE "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Bahrain. */ +static const BuiltinFormat spBuiltinFormats_ar_BH[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_BH "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Algeria. */ +static const BuiltinFormat spBuiltinFormats_ar_DZ[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_DZ "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Egypt. */ +static const BuiltinFormat spBuiltinFormats_ar_EG[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_EG "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Iraq. */ +static const BuiltinFormat spBuiltinFormats_ar_IQ[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_IQ "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Jordan. */ +static const BuiltinFormat spBuiltinFormats_ar_JO[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_JO "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Kuwait. */ +static const BuiltinFormat spBuiltinFormats_ar_KW[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_KW "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Lebanon. */ +static const BuiltinFormat spBuiltinFormats_ar_LB[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_LB "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Libya. */ +static const BuiltinFormat spBuiltinFormats_ar_LY[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_LY "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Morocco. */ +static const BuiltinFormat spBuiltinFormats_ar_MA[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_MA "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Oman. */ +static const BuiltinFormat spBuiltinFormats_ar_OM[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_OM "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Qatar. */ +static const BuiltinFormat spBuiltinFormats_ar_QA[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_QA "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Saudi Arabia. */ +static const BuiltinFormat spBuiltinFormats_ar_SA[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_SA "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Syria. */ +static const BuiltinFormat spBuiltinFormats_ar_SY[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_SY "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Tunisia. */ +static const BuiltinFormat spBuiltinFormats_ar_TN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_TN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Arabic, Yemen. */ +static const BuiltinFormat spBuiltinFormats_ar_YE[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_YE "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Belarusian, Belarus. */ +static const BuiltinFormat spBuiltinFormats_be_BY[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Bulgarian, Bulgaria. */ +static const BuiltinFormat spBuiltinFormats_bg_BG[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.M.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_L_LC UTF8_CYR_W_LC "\"", "_" UTF8_CYR_L_LC "_" UTF8_CYR_W_LC, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Bengali, India. */ +static const BuiltinFormat spBuiltinFormats_bn_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_BN_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Czech, Czech Republic. */ +static const BuiltinFormat spBuiltinFormats_cs_CZ[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"K" UTF8_CCARON_LC "\"", "_K_" UTF8_CCARON_LC, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Danish, Denmark. */ +static const BuiltinFormat spBuiltinFormats_da_DK[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** German, Austria. */ +static const BuiltinFormat spBuiltinFormats_de_AT[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** German, Switzerland. */ +static const BuiltinFormat spBuiltinFormats_de_CH[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** German, Germany. */ +static const BuiltinFormat spBuiltinFormats_de_DE[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** German, Liechtenstein. */ +static const BuiltinFormat spBuiltinFormats_de_LI[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"CHF\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** German, Luxembourg. */ +static const BuiltinFormat spBuiltinFormats_de_LU[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Divehi, Maldives. */ +static const BuiltinFormat spBuiltinFormats_div_MV[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_NUMBER_SYMBOL_MINUS( "\"" UTF8_RUFIYAA ".\"", "_" UTF8_RUFIYAA "_.", " " ), + NUMFMT_ENDTABLE() +}; + +/** Greek, Greece. */ +static const BuiltinFormat spBuiltinFormats_el_GR[] = +{ + NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** English, Australia. */ +static const BuiltinFormat spBuiltinFormats_en_AU[] = +{ + NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Belize. */ +static const BuiltinFormat spBuiltinFormats_en_BZ[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"BZ$\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Canada. */ +static const BuiltinFormat spBuiltinFormats_en_CA[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Caribbean. */ +static const BuiltinFormat spBuiltinFormats_en_CB[] = +{ + NUMFMT_ALLDATETIMES( "MM/DD/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, United Kingdom. */ +static const BuiltinFormat spBuiltinFormats_en_GB[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_POUND_GB, "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Ireland. */ +static const BuiltinFormat spBuiltinFormats_en_IE[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Jamaica. */ +static const BuiltinFormat spBuiltinFormats_en_JM[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"J$\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, New Zealand. */ +static const BuiltinFormat spBuiltinFormats_en_NZ[] = +{ + NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Philippines. */ +static const BuiltinFormat spBuiltinFormats_en_PH[] = +{ + NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Php\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, Trinidad and Tobago. */ +static const BuiltinFormat spBuiltinFormats_en_TT[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"TT$\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, USA. */ +static const BuiltinFormat spBuiltinFormats_en_US[] = +{ + NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** English, South Africa. */ +static const BuiltinFormat spBuiltinFormats_en_ZA[] = +{ + NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\\R", " " ), + NUMFMT_ENDTABLE() +}; + +/** English, Zimbabwe. */ +static const BuiltinFormat spBuiltinFormats_en_ZW[] = +{ + NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Z$\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Argentina. */ +static const BuiltinFormat spBuiltinFormats_es_AR[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "$", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Bolivia. */ +static const BuiltinFormat spBuiltinFormats_es_BO[] = +{ + // slashes must be quoted to prevent conversion to minus + NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"$b\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Chile. */ +static const BuiltinFormat spBuiltinFormats_es_CL[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Colombia. */ +static const BuiltinFormat spBuiltinFormats_es_CO[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Costa Rica. */ +static const BuiltinFormat spBuiltinFormats_es_CR[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( UTF8_COLON, "" ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Dominican Republic. */ +static const BuiltinFormat spBuiltinFormats_es_DO[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"RD$\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Ecuador. */ +static const BuiltinFormat spBuiltinFormats_es_EC[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Spain. */ +static const BuiltinFormat spBuiltinFormats_es_ES[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Guatemala. */ +static const BuiltinFormat spBuiltinFormats_es_GT[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\Q", "" ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Honduras. */ +static const BuiltinFormat spBuiltinFormats_es_HN[] = +{ + // slashes must be quoted to prevent conversion to minus + NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"L.\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Mexico. */ +static const BuiltinFormat spBuiltinFormats_es_MX[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Nicaragua. */ +static const BuiltinFormat spBuiltinFormats_es_NI[] = +{ + // slashes must be quoted to prevent conversion to minus + NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"C$\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Panama. */ +static const BuiltinFormat spBuiltinFormats_es_PA[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"B/.\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Peru. */ +static const BuiltinFormat spBuiltinFormats_es_PE[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"S/.\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Puerto Rico. */ +static const BuiltinFormat spBuiltinFormats_es_PR[] = +{ + // slashes must be quoted to prevent conversion to minus + NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Paraguay. */ +static const BuiltinFormat spBuiltinFormats_es_PY[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Gs\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, El Salvador. */ +static const BuiltinFormat spBuiltinFormats_es_SV[] = +{ + // slashes must be quoted to prevent conversion to minus + NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Uruguay. */ +static const BuiltinFormat spBuiltinFormats_es_UY[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"$U\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Spanish, Venezuela. */ +static const BuiltinFormat spBuiltinFormats_es_VE[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "Bs", " " ), + NUMFMT_ENDTABLE() +}; + +/** Estonian, Estonia. */ +static const BuiltinFormat spBuiltinFormats_et_EE[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "D.MM.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr\"", "_k_r", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Farsi, Iran. */ +static const BuiltinFormat spBuiltinFormats_fa_IR[] = +{ + NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_FA_IR "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Finnish, Finland. */ +static const BuiltinFormat spBuiltinFormats_fi_FI[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_STRING( 9, "0\\ %" ), + NUMFMT_STRING( 10, "0.00\\ %" ), + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Faroese, Faroe Islands. */ +static const BuiltinFormat spBuiltinFormats_fo_FO[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** French, Belgium. */ +static const BuiltinFormat spBuiltinFormats_fr_BE[] = +{ + NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** French, Canada. */ +static const BuiltinFormat spBuiltinFormats_fr_CA[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_NUMBER_SYMBOL_CLOSE( "$", "_$", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** French, Switzerland. */ +static const BuiltinFormat spBuiltinFormats_fr_CH[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** French, France. */ +static const BuiltinFormat spBuiltinFormats_fr_FR[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** French, Luxembourg. */ +static const BuiltinFormat spBuiltinFormats_fr_LU[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** French, Monaco. */ +static const BuiltinFormat spBuiltinFormats_fr_MC[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Galizian, Spain. */ +static const BuiltinFormat spBuiltinFormats_gl_ES[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Gujarati, India. */ +static const BuiltinFormat spBuiltinFormats_gu_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_GU_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Hebrew, Israel. */ +static const BuiltinFormat spBuiltinFormats_he_IL[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( UTF8_SHEQEL, " " ), + NUMFMT_ENDTABLE() +}; + +/** Hindi, India. */ +static const BuiltinFormat spBuiltinFormats_hi_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_HI_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Croatian, Bosnia and Herzegowina. */ +static const BuiltinFormat spBuiltinFormats_hr_BA[] = +{ + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"KM\"", "_K_M", " " ), + NUMFMT_ENDTABLE() +}; + +/** Croatian, Croatia. */ +static const BuiltinFormat spBuiltinFormats_hr_HR[] = +{ + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kn\"", "_k_n", " " ), + NUMFMT_ENDTABLE() +}; + +/** Hungarian, Hungary. */ +static const BuiltinFormat spBuiltinFormats_hu_HU[] = +{ + // space character is group separator, literal spaces must be quoted + // MMM is rendered differently in Calc and Excel (see #i41488#) + NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Ft\"", "_F_t", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Armenian, Armenia. */ +static const BuiltinFormat spBuiltinFormats_hy_AM[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_HY_DA_LC UTF8_HY_REH_LC ".\"", "_" UTF8_HY_DA_LC "_" UTF8_HY_REH_LC "_.", " " ), + NUMFMT_ENDTABLE() +}; + +/** Indonesian, Indonesia. */ +static const BuiltinFormat spBuiltinFormats_id_ID[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Rp\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** Icelandic, Iceland. */ +static const BuiltinFormat spBuiltinFormats_is_IS[] = +{ + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr.\"", "_k_r_.", " " ), + NUMFMT_ENDTABLE() +}; + +/** Italian, Switzerland. */ +static const BuiltinFormat spBuiltinFormats_it_CH[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Italian, Italy. */ +static const BuiltinFormat spBuiltinFormats_it_IT[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Georgian, Georgia. */ +static const BuiltinFormat spBuiltinFormats_ka_GE[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Lari\"", "_L_a_r_i", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Kazakh, Kazakhstan. */ +static const BuiltinFormat spBuiltinFormats_kk_KZ[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\\T", "" ), + NUMFMT_ENDTABLE() +}; + +/** Kannada, India. */ +static const BuiltinFormat spBuiltinFormats_kn_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_KN_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Kyrgyz, Kyrgyzstan. */ +static const BuiltinFormat spBuiltinFormats_ky_KG[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_S_LC UTF8_CYR_O_LC UTF8_CYR_M_LC "\"", "_" UTF8_CYR_S_LC "_" UTF8_CYR_O_LC "_" UTF8_CYR_M_LC, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Lithuanian, Lithuania. */ +static const BuiltinFormat spBuiltinFormats_lt_LT[] = +{ + NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Lt\"", "_L_t", " " ), + NUMFMT_ENDTABLE() +}; + +/** Latvian, Latvia. */ +static const BuiltinFormat spBuiltinFormats_lv_LV[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"Ls\"", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Malayalam, India. */ +static const BuiltinFormat spBuiltinFormats_ml_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_ML_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Mongolian, Mongolia. */ +static const BuiltinFormat spBuiltinFormats_mn_MN[] = +{ + NUMFMT_ALLDATETIMES( "YY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_TUGRUG, "_" UTF8_TUGRUG, "" ), + NUMFMT_ENDTABLE() +}; + +/** Malay, Brunei Darussalam. */ +static const BuiltinFormat spBuiltinFormats_ms_BN[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ), + NUMFMT_ENDTABLE() +}; + +/** Malay, Malaysia. */ +static const BuiltinFormat spBuiltinFormats_ms_MY[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\R", "" ), + NUMFMT_ENDTABLE() +}; + +/** Maltese, Malta. */ +static const BuiltinFormat spBuiltinFormats_mt_MT[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"Lm\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** Dutch, Belgium. */ +static const BuiltinFormat spBuiltinFormats_nl_BE[] = +{ + // slashes must be quoted to prevent conversion to minus + NUMFMT_ALLDATETIMES( "D\\/MM\\/YYYY", "D", "\\/", "MMM", "\\/", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Dutch, Netherlands. */ +static const BuiltinFormat spBuiltinFormats_nl_NL[] = +{ + NUMFMT_ALLDATETIMES( "D-M-YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Norwegian (Bokmal and Nynorsk), Norway. */ +static const BuiltinFormat spBuiltinFormats_no_NO[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Punjabi, India. */ +static const BuiltinFormat spBuiltinFormats_pa_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_PA_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Polish, Poland. */ +static const BuiltinFormat spBuiltinFormats_pl_PL[] = +{ + // space character is group separator, literal spaces must be quoted + // MMM is rendered differently in Calc and Excel (see #i72300#) + NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"z" UTF8_LSTROKE_LC "\"", "_z_" UTF8_LSTROKE_LC, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Portugese, Brazil. */ +static const BuiltinFormat spBuiltinFormats_pt_BR[] = +{ + NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "/", "MMM", "/", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"R$\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Portugese, Portugal. */ +static const BuiltinFormat spBuiltinFormats_pt_PT[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ), + NUMFMT_ENDTABLE() +}; + +/** Romanian, Romania. */ +static const BuiltinFormat spBuiltinFormats_ro_RO[] = +{ + // space character is group separator, literal spaces must be quoted (but see #i75367#) + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"lei\"", "_l_e_i", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Russian, Russian Federation. */ +static const BuiltinFormat spBuiltinFormats_ru_RU[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "" ), + NUMFMT_ENDTABLE() +}; + +/** Slovak, Slovakia. */ +static const BuiltinFormat spBuiltinFormats_sk_SK[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Sk\"", "_S_k", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Slovenian, Slovenia. */ +static const BuiltinFormat spBuiltinFormats_sl_SI[] = +{ + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"SIT\"", "_S_I_T", " " ), + NUMFMT_ENDTABLE() +}; + +/** Swedish, Finland. */ +static const BuiltinFormat spBuiltinFormats_sv_FI[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_STRING( 9, "0\\ %" ), + NUMFMT_STRING( 10, "0.00\\ %" ), + NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Swedish, Sweden. */ +static const BuiltinFormat spBuiltinFormats_sv_SE[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr\"", "_k_r", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Swahili, Tanzania. */ +static const BuiltinFormat spBuiltinFormats_sw_TZ[] = +{ + NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\S", "" ), + NUMFMT_ENDTABLE() +}; + +/** Tamil, India. */ +static const BuiltinFormat spBuiltinFormats_ta_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_TA_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Telugu, India. */ +static const BuiltinFormat spBuiltinFormats_te_IN[] = +{ + NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_TE_IN "\"", " " ), + NUMFMT_ENDTABLE() +}; + +/** Thai, Thailand. */ +static const BuiltinFormat spBuiltinFormats_th_TH[] = +{ + NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_BAHT, "" ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 63, UTF8_BAHT, "", "t" ), + NUMFMT_STRING( 59, "t0" ), + NUMFMT_STRING( 60, "t0.00" ), + NUMFMT_STRING( 61, "t#,##0" ), + NUMFMT_STRING( 62, "t#,##0.00" ), + NUMFMT_STRING( 67, "t0%" ), + NUMFMT_STRING( 68, "t0.00%" ), + NUMFMT_STRING( 69, "t# ?/?" ), + NUMFMT_STRING( 70, "t# ?\?/?\?" ), + NUMFMT_STRING( 71, "tD/M/EE" ), + NUMFMT_STRING( 72, "tD-MMM-E" ), + NUMFMT_STRING( 73, "tD-MMM" ), + NUMFMT_STRING( 74, "tMMM-E" ), + NUMFMT_STRING( 75, "th:mm" ), + NUMFMT_STRING( 76, "th:mm:ss" ), + NUMFMT_STRING( 77, "tD/M/EE h:mm" ), + NUMFMT_STRING( 78, "tmm:ss" ), + NUMFMT_STRING( 79, "t[h]:mm:ss" ), + NUMFMT_STRING( 80, "tmm:ss.0" ), + NUMFMT_STRING( 81, "D/M/E" ), + NUMFMT_ENDTABLE() +}; + +/** Turkish, Turkey. */ +static const BuiltinFormat spBuiltinFormats_tr_TR[] = +{ + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"TL\"", "_T_L", " " ), + NUMFMT_ENDTABLE() +}; + +/** Tatar, Russian Federation. */ +static const BuiltinFormat spBuiltinFormats_tt_RU[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Ukrainian, Ukraine. */ +static const BuiltinFormat spBuiltinFormats_uk_UA[] = +{ + // space character is group separator, literal spaces must be quoted + NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_G_LC UTF8_CYR_R_LC UTF8_CYR_N_LC ".\"", "_" UTF8_CYR_G_LC "_" UTF8_CYR_R_LC "_" UTF8_CYR_N_LC "_.", "\\ " ), + NUMFMT_ENDTABLE() +}; + +/** Urdu, Pakistan. */ +static const BuiltinFormat spBuiltinFormats_ur_PK[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"Rs\"", "" ), + NUMFMT_ENDTABLE() +}; + +/** Vietnamese, Viet Nam. */ +static const BuiltinFormat spBuiltinFormats_vi_VN[] = +{ + NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_DONG, "_" UTF8_DONG, " " ), + NUMFMT_ENDTABLE() +}; + +// CJK ------------------------------------------------------------------------ + +/** Base table for CJK locales. */ +static const BuiltinFormat spBuiltinFormats_CJK[] = +{ + NUMFMT_REUSE( 29, 28 ), + NUMFMT_REUSE( 36, 27 ), + NUMFMT_REUSE( 50, 27 ), + NUMFMT_REUSE( 51, 28 ), + NUMFMT_REUSE( 52, 34 ), + NUMFMT_REUSE( 53, 35 ), + NUMFMT_REUSE( 54, 28 ), + NUMFMT_REUSE( 55, 34 ), + NUMFMT_REUSE( 56, 35 ), + NUMFMT_REUSE( 57, 27 ), + NUMFMT_REUSE( 58, 28 ), + NUMFMT_ENDTABLE() +}; + +/** Japanese, Japan. */ +static const BuiltinFormat spBuiltinFormats_ja_JP[] = +{ + NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_YEN_JP, "" ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ), + NUMFMT_STRING( 27, "[$-411]GE.MM.DD" ), + NUMFMT_STRING( 28, "[$-411]GGGE\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ), + NUMFMT_STRING( 30, "MM/DD/YY" ), + NUMFMT_STRING( 31, "YYYY\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ), + NUMFMT_TIME_CJK( 32, "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ), + NUMFMT_STRING( 34, "YYYY\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"" ), + NUMFMT_STRING( 35, "MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ), + NUMFMT_ENDTABLE() +}; + +/** Korean, South Korea. */ +static const BuiltinFormat spBuiltinFormats_ko_KR[] = +{ + NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_WON, "" ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ), + NUMFMT_STRING( 27, "YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ), + NUMFMT_STRING( 28, "MM-DD" ), + NUMFMT_STRING( 30, "MM-DD-YY" ), + NUMFMT_STRING( 31, "YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ), + NUMFMT_TIME_CJK( 32, "h", UTF8_KO_HOUR, UTF8_KO_MIN, UTF8_KO_SEC ), + // slashes must be quoted to prevent conversion to minus + NUMFMT_STRING( 34, "YYYY\\/MM\\/DD" ), + NUMFMT_REUSE( 35, 14 ), + NUMFMT_ENDTABLE() +}; + +/** Chinese, China. */ +static const BuiltinFormat spBuiltinFormats_zh_CN[] = +{ + NUMFMT_ALLDATETIMES( "YYYY-M-D", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( UTF8_YEN_CN, "" ), + NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CS_HOUR, UTF8_CS_MIN, UTF8_CS_SEC ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ), + NUMFMT_STRING( 27, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"" ), + NUMFMT_STRING( 28, "M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ), + NUMFMT_STRING( 30, "M-D-YY" ), + NUMFMT_STRING( 31, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ), + NUMFMT_REUSE( 52, 27 ), + NUMFMT_REUSE( 53, 28 ), + NUMFMT_ENDTABLE() +}; + +/** Chinese, Hong Kong. */ +static const BuiltinFormat spBuiltinFormats_zh_HK[] = +{ + NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"HK$\"", "" ), + NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ), + NUMFMT_STRING( 27, "[$-404]D/M/E" ), + NUMFMT_STRING( 28, "[$-404]D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"E\"" UTF8_CJ_YEAR "\"" ), + NUMFMT_STRING( 30, "M/D/YY" ), + NUMFMT_STRING( 31, "D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"YYYY\"" UTF8_CJ_YEAR "\"" ), + NUMFMT_ENDTABLE() +}; + +/** Chinese, Macau. */ +static const BuiltinFormat spBuiltinFormats_zh_MO[] = +{ + NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\P", "" ), + NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ), + NUMFMT_STRING( 27, "[$-404]D/M/E" ), + NUMFMT_STRING( 28, "[$-404]D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"E\"" UTF8_CJ_YEAR "\"" ), + NUMFMT_STRING( 30, "M/D/YY" ), + NUMFMT_STRING( 31, "D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"YYYY\"" UTF8_CJ_YEAR "\"" ), + NUMFMT_ENDTABLE() +}; + +/** Chinese, Singapore. */ +static const BuiltinFormat spBuiltinFormats_zh_SG[] = +{ + NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ), + NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ), + NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CS_HOUR, UTF8_CS_MIN, UTF8_CS_SEC ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ), + NUMFMT_STRING( 27, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"" ), + NUMFMT_STRING( 28, "M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ), + NUMFMT_STRING( 30, "M/D/YY" ), + NUMFMT_STRING( 31, "D\"" UTF8_CS_DAY "\"M\"" UTF8_CS_MON "\"YYYY\"" UTF8_CS_YEAR "\"" ), + NUMFMT_ENDTABLE() +}; + +/** Chinese, Taiwan. */ +static const BuiltinFormat spBuiltinFormats_zh_TW[] = +{ + NUMFMT_ALLDATETIMES( "YYYY/M/D", "D", "-", "MMM", "-", "YY", "hh", "hh" ), + NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ), + NUMFMT_ALLTIMES_CJK( "hh", "hh", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ), + NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ), + NUMFMT_STRING( 27, "[$-404]E/M/D" ), + NUMFMT_STRING( 28, "[$-404]E\"" UTF8_CJ_YEAR "\"M\"" UTF8_CJ_MON "\"D\"" UTF8_CJ_DAY "\"" ), + NUMFMT_STRING( 30, "M/D/YY" ), + NUMFMT_STRING( 31, "YYYY\"" UTF8_CJ_YEAR "\"M\"" UTF8_CJ_MON "\"D\"" UTF8_CJ_DAY "\"" ), + NUMFMT_ENDTABLE() +}; + +// ---------------------------------------------------------------------------- + +/** Specifies a built-in number format table for a specific locale. */ +struct BuiltinFormatTable +{ + const sal_Char* mpcLocale; /// The locale for this table. + const sal_Char* mpcParent; /// The locale of the parent table. + const BuiltinFormat* mpFormats; /// The number format table (may be 0, if equal to parent). +}; + +static const BuiltinFormatTable spBuiltinFormatTables[] = +{ // locale parent format table + { "*", "", spBuiltinFormats_BASE }, // Base table + { "af-ZA", "*", spBuiltinFormats_en_ZA }, // Afrikaans, South Africa + { "ar-AE", "*", spBuiltinFormats_ar_AE }, // Arabic, U.A.E. + { "ar-BH", "*", spBuiltinFormats_ar_BH }, // Arabic, Bahrain + { "ar-DZ", "*", spBuiltinFormats_ar_DZ }, // Arabic, Algeria + { "ar-EG", "*", spBuiltinFormats_ar_EG }, // Arabic, Egypt + { "ar-IQ", "*", spBuiltinFormats_ar_IQ }, // Arabic, Iraq + { "ar-JO", "*", spBuiltinFormats_ar_JO }, // Arabic, Jordan + { "ar-KW", "*", spBuiltinFormats_ar_KW }, // Arabic, Kuwait + { "ar-LB", "*", spBuiltinFormats_ar_LB }, // Arabic, Lebanon + { "ar-LY", "*", spBuiltinFormats_ar_LY }, // Arabic, Libya + { "ar-MA", "*", spBuiltinFormats_ar_MA }, // Arabic, Morocco + { "ar-OM", "*", spBuiltinFormats_ar_OM }, // Arabic, Oman + { "ar-QA", "*", spBuiltinFormats_ar_QA }, // Arabic, Qatar + { "ar-SA", "*", spBuiltinFormats_ar_SA }, // Arabic, Saudi Arabia + { "ar-SY", "*", spBuiltinFormats_ar_SY }, // Arabic, Syria + { "ar-TN", "*", spBuiltinFormats_ar_TN }, // Arabic, Tunisia + { "ar-YE", "*", spBuiltinFormats_ar_YE }, // Arabic, Yemen + { "be-BY", "*", spBuiltinFormats_be_BY }, // Belarusian, Belarus + { "bg-BG", "*", spBuiltinFormats_bg_BG }, // Bulgarian, Bulgaria + { "bn-IN", "*", spBuiltinFormats_bn_IN }, // Bengali, India + { "ca-ES", "*", spBuiltinFormats_es_ES }, // Catalan, Spain + { "cs-CZ", "*", spBuiltinFormats_cs_CZ }, // Czech, Czech Republic + { "cy-GB", "*", spBuiltinFormats_en_GB }, // Welsh, United Kingdom + { "da-DK", "*", spBuiltinFormats_da_DK }, // Danish, Denmark + { "de-AT", "*", spBuiltinFormats_de_AT }, // German, Austria + { "de-CH", "*", spBuiltinFormats_de_CH }, // German, Switzerland + { "de-DE", "*", spBuiltinFormats_de_DE }, // German, Germany + { "de-LI", "*", spBuiltinFormats_de_LI }, // German, Liechtenstein + { "de-LU", "*", spBuiltinFormats_de_LU }, // German, Luxembourg + { "div-MV", "*", spBuiltinFormats_div_MV }, // Divehi, Maldives + { "el-GR", "*", spBuiltinFormats_el_GR }, // Greek, Greece + { "en-AU", "*", spBuiltinFormats_en_AU }, // English, Australia + { "en-BZ", "*", spBuiltinFormats_en_BZ }, // English, Belize + { "en-CA", "*", spBuiltinFormats_en_CA }, // English, Canada + { "en-CB", "*", spBuiltinFormats_en_CB }, // English, Caribbean + { "en-GB", "*", spBuiltinFormats_en_GB }, // English, United Kingdom + { "en-IE", "*", spBuiltinFormats_en_IE }, // English, Ireland + { "en-JM", "*", spBuiltinFormats_en_JM }, // English, Jamaica + { "en-NZ", "*", spBuiltinFormats_en_NZ }, // English, New Zealand + { "en-PH", "*", spBuiltinFormats_en_PH }, // English, Philippines + { "en-TT", "*", spBuiltinFormats_en_TT }, // English, Trinidad and Tobago + { "en-US", "*", spBuiltinFormats_en_US }, // English, USA + { "en-ZA", "*", spBuiltinFormats_en_ZA }, // English, South Africa + { "en-ZW", "*", spBuiltinFormats_en_ZW }, // English, Zimbabwe + { "es-AR", "*", spBuiltinFormats_es_AR }, // Spanish, Argentina + { "es-BO", "*", spBuiltinFormats_es_BO }, // Spanish, Bolivia + { "es-CL", "*", spBuiltinFormats_es_CL }, // Spanish, Chile + { "es-CO", "*", spBuiltinFormats_es_CO }, // Spanish, Colombia + { "es-CR", "*", spBuiltinFormats_es_CR }, // Spanish, Costa Rica + { "es-DO", "*", spBuiltinFormats_es_DO }, // Spanish, Dominican Republic + { "es-EC", "*", spBuiltinFormats_es_EC }, // Spanish, Ecuador + { "es-ES", "*", spBuiltinFormats_es_ES }, // Spanish, Spain + { "es-GT", "*", spBuiltinFormats_es_GT }, // Spanish, Guatemala + { "es-HN", "*", spBuiltinFormats_es_HN }, // Spanish, Honduras + { "es-MX", "*", spBuiltinFormats_es_MX }, // Spanish, Mexico + { "es-NI", "*", spBuiltinFormats_es_NI }, // Spanish, Nicaragua + { "es-PA", "*", spBuiltinFormats_es_PA }, // Spanish, Panama + { "es-PE", "*", spBuiltinFormats_es_PE }, // Spanish, Peru + { "es-PR", "*", spBuiltinFormats_es_PR }, // Spanish, Puerto Rico + { "es-PY", "*", spBuiltinFormats_es_PY }, // Spanish, Paraguay + { "es-SV", "*", spBuiltinFormats_es_SV }, // Spanish, El Salvador + { "es-UY", "*", spBuiltinFormats_es_UY }, // Spanish, Uruguay + { "es-VE", "*", spBuiltinFormats_es_VE }, // Spanish, Venezuela + { "et-EE", "*", spBuiltinFormats_et_EE }, // Estonian, Estonia + { "fa-IR", "*", spBuiltinFormats_fa_IR }, // Farsi, Iran + { "fi-FI", "*", spBuiltinFormats_fi_FI }, // Finnish, Finland + { "fo-FO", "*", spBuiltinFormats_fo_FO }, // Faroese, Faroe Islands + { "fr-BE", "*", spBuiltinFormats_fr_BE }, // French, Belgium + { "fr-CA", "*", spBuiltinFormats_fr_CA }, // French, Canada + { "fr-CH", "*", spBuiltinFormats_fr_CH }, // French, Switzerland + { "fr-FR", "*", spBuiltinFormats_fr_FR }, // French, France + { "fr-LU", "*", spBuiltinFormats_fr_LU }, // French, Luxembourg + { "fr-MC", "*", spBuiltinFormats_fr_MC }, // French, Monaco + { "gl-ES", "*", spBuiltinFormats_gl_ES }, // Galizian, Spain + { "gu-IN", "*", spBuiltinFormats_gu_IN }, // Gujarati, India + { "he-IL", "*", spBuiltinFormats_he_IL }, // Hebrew, Israel + { "hi-IN", "*", spBuiltinFormats_hi_IN }, // Hindi, India + { "hr-BA", "*", spBuiltinFormats_hr_BA }, // Croatian, Bosnia and Herzegowina + { "hr-HR", "*", spBuiltinFormats_hr_HR }, // Croatian, Croatia + { "hu-HU", "*", spBuiltinFormats_hu_HU }, // Hungarian, Hungary + { "hy-AM", "*", spBuiltinFormats_hy_AM }, // Armenian, Armenia + { "id-ID", "*", spBuiltinFormats_id_ID }, // Indonesian, Indonesia + { "is-IS", "*", spBuiltinFormats_is_IS }, // Icelandic, Iceland + { "it-CH", "*", spBuiltinFormats_it_CH }, // Italian, Switzerland + { "it-IT", "*", spBuiltinFormats_it_IT }, // Italian, Italy + { "ka-GE", "*", spBuiltinFormats_ka_GE }, // Georgian, Georgia + { "kk-KZ", "*", spBuiltinFormats_kk_KZ }, // Kazakh, Kazakhstan + { "kn-IN", "*", spBuiltinFormats_kn_IN }, // Kannada, India + { "kok-IN", "*", spBuiltinFormats_hi_IN }, // Konkani, India + { "ky-KG", "*", spBuiltinFormats_ky_KG }, // Kyrgyz, Kyrgyzstan + { "lt-LT", "*", spBuiltinFormats_lt_LT }, // Lithuanian, Lithuania + { "lv-LV", "*", spBuiltinFormats_lv_LV }, // Latvian, Latvia + { "mi-NZ", "*", spBuiltinFormats_en_NZ }, // Maori, New Zealand + { "ml-IN", "*", spBuiltinFormats_ml_IN }, // Malayalam, India + { "mn-MN", "*", spBuiltinFormats_mn_MN }, // Mongolian, Mongolia + { "mr-IN", "*", spBuiltinFormats_hi_IN }, // Marathi, India + { "ms-BN", "*", spBuiltinFormats_ms_BN }, // Malay, Brunei Darussalam + { "ms-MY", "*", spBuiltinFormats_ms_MY }, // Malay, Malaysia + { "mt-MT", "*", spBuiltinFormats_mt_MT }, // Maltese, Malta + { "nb-NO", "*", spBuiltinFormats_no_NO }, // Norwegian Bokmal, Norway + { "nl-BE", "*", spBuiltinFormats_nl_BE }, // Dutch, Belgium + { "nl-NL", "*", spBuiltinFormats_nl_NL }, // Dutch, Netherlands + { "nn-NO", "*", spBuiltinFormats_no_NO }, // Norwegian Nynorsk, Norway + { "nso-ZA", "*", spBuiltinFormats_en_ZA }, // Northern Sotho, South Africa + { "pa-IN", "*", spBuiltinFormats_pa_IN }, // Punjabi, India + { "pl-PL", "*", spBuiltinFormats_pl_PL }, // Polish, Poland + { "pt-BR", "*", spBuiltinFormats_pt_BR }, // Portugese, Brazil + { "pt-PT", "*", spBuiltinFormats_pt_PT }, // Portugese, Portugal + { "qu-BO", "*", spBuiltinFormats_es_BO }, // Quechua, Bolivia + { "qu-EC", "*", spBuiltinFormats_es_EC }, // Quechua, Ecuador + { "qu-PE", "*", spBuiltinFormats_es_PE }, // Quechua, Peru + { "ro-RO", "*", spBuiltinFormats_ro_RO }, // Romanian, Romania + { "ru-RU", "*", spBuiltinFormats_ru_RU }, // Russian, Russian Federation + { "sa-IN", "*", spBuiltinFormats_hi_IN }, // Sanskrit, India + { "se-FI", "*", spBuiltinFormats_fi_FI }, // Sami, Finland + { "se-NO", "*", spBuiltinFormats_no_NO }, // Sami, Norway + { "se-SE", "*", spBuiltinFormats_sv_SE }, // Sami, Sweden + { "sk-SK", "*", spBuiltinFormats_sk_SK }, // Slovak, Slovakia + { "sl-SI", "*", spBuiltinFormats_sl_SI }, // Slovenian, Slovenia + { "sv-FI", "*", spBuiltinFormats_sv_FI }, // Swedish, Finland + { "sv-SE", "*", spBuiltinFormats_sv_SE }, // Swedish, Sweden + { "sw-TZ", "*", spBuiltinFormats_sw_TZ }, // Swahili, Tanzania + { "syr-SY", "*", spBuiltinFormats_ar_SY }, // Syriac, Syria + { "syr-TR", "*", spBuiltinFormats_tr_TR }, // Syriac, Turkey + { "ta-IN", "*", spBuiltinFormats_ta_IN }, // Tamil, India + { "te-IN", "*", spBuiltinFormats_te_IN }, // Telugu, India + { "th-TH", "*", spBuiltinFormats_th_TH }, // Thai, Thailand + { "tn-ZA", "*", spBuiltinFormats_en_ZA }, // Tswana, South Africa + { "tr-TR", "*", spBuiltinFormats_tr_TR }, // Turkish, Turkey + { "tt-RU", "*", spBuiltinFormats_tt_RU }, // Tatar, Russian Federation + { "uk-UA", "*", spBuiltinFormats_uk_UA }, // Ukrainian, Ukraine + { "ur-PK", "*", spBuiltinFormats_ur_PK }, // Urdu, Pakistan + { "vi-VN", "*", spBuiltinFormats_vi_VN }, // Vietnamese, Viet Nam + { "xh-ZA", "*", spBuiltinFormats_en_ZA }, // Xhosa, South Africa + { "zu-ZA", "*", spBuiltinFormats_en_ZA }, // Zulu, South Africa + + { "*CJK", "*", spBuiltinFormats_CJK }, // CJK base table + { "ja-JP", "*CJK", spBuiltinFormats_ja_JP }, // Japanese, Japan + { "ko-KR", "*CJK", spBuiltinFormats_ko_KR }, // Korean, South Korea + { "zh-CN", "*CJK", spBuiltinFormats_zh_CN }, // Chinese, China + { "zh-HK", "*CJK", spBuiltinFormats_zh_HK }, // Chinese, Hong Kong + { "zh-MO", "*CJK", spBuiltinFormats_zh_MO }, // Chinese, Macau + { "zh-SG", "*CJK", spBuiltinFormats_zh_SG }, // Chinese, Singapore + { "zh-TW", "*CJK", spBuiltinFormats_zh_TW } // Chinese, Taiwan +}; + +// ============================================================================ + +/** Functor for converting an XML number format to an API number format index. */ +class NumberFormatFunctor +{ +public: + explicit NumberFormatFunctor( const WorkbookHelper& rHelper ); + + inline bool is() const { return mxNumFmts.is(); } + + inline void operator()( NumberFormat& rNumFmt ) const + { rNumFmt.finalizeImport( mxNumFmts, maEnUsLocale ); } + +private: + Reference< XNumberFormats > mxNumFmts; + Locale maEnUsLocale; +}; + +// ---------------------------------------------------------------------------- + +NumberFormatFunctor::NumberFormatFunctor( const WorkbookHelper& rHelper ) : + maEnUsLocale( CREATE_OUSTRING( "en" ), CREATE_OUSTRING( "US" ), OUString() ) +{ + try + { + Reference< XNumberFormatsSupplier > xNumFmtsSupp( rHelper.getDocument(), UNO_QUERY_THROW ); + mxNumFmts = xNumFmtsSupp->getNumberFormats(); + } + catch( Exception& ) + { + } + OSL_ENSURE( mxNumFmts.is(), "NumberFormatFunctor::NumberFormatFunctor - cannot get number formats" ); +} + +} // namespace + +// ============================================================================ + +OoxNumFmtData::OoxNumFmtData() : + mnPredefId( -1 ) +{ +} + +// ---------------------------------------------------------------------------- + +namespace { + +sal_Int32 lclCreatePredefinedFormat( const Reference< XNumberFormats >& rxNumFmts, + sal_Int16 nPredefId, const Locale& rToLocale ) +{ + sal_Int32 nIndex = 0; + try + { + Reference< XNumberFormatTypes > xNumFmtTypes( rxNumFmts, UNO_QUERY_THROW ); + nIndex = (nPredefId >= 0) ? + xNumFmtTypes->getFormatIndex( nPredefId, rToLocale ) : + xNumFmtTypes->getStandardIndex( rToLocale ); + } + catch( Exception& ) + { + OSL_ENSURE( false, + OStringBuffer( "lclCreatePredefinedFormat - cannot create predefined number format " ). + append( OString::valueOf( static_cast< sal_Int32 >( nPredefId ) ) ).getStr() ); + } + return nIndex; +} + +sal_Int32 lclCreateFormat( const Reference< XNumberFormats >& rxNumFmts, + const OUString& rFmtCode, const Locale& rToLocale, const Locale& rFromLocale ) +{ + sal_Int32 nIndex = 0; + try + { + nIndex = rxNumFmts->addNewConverted( rFmtCode, rFromLocale, rToLocale ); + } + catch( Exception& ) + { + // BIFF2-BIFF4 stores standard format explicitly in stream + static const OUString saGeneral = CREATE_OUSTRING( "general" ); + if( rFmtCode.equalsIgnoreAsciiCase( saGeneral ) ) + { + nIndex = lclCreatePredefinedFormat( rxNumFmts, 0, rToLocale ); + } + else + { + OSL_ENSURE( false, + OStringBuffer( "lclCreateFormat - cannot create number format '" ). + append( OUStringToOString( rFmtCode, osl_getThreadTextEncoding() ) ). + append( '\'' ).getStr() ); + } + } + return nIndex; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +NumberFormat::NumberFormat( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void NumberFormat::setFormatCode( const OUString& rFmtCode ) +{ + maOoxData.maFmtCode = rFmtCode; +} + +void NumberFormat::setFormatCode( const Locale& rLocale, const sal_Char* pcFmtCode ) +{ + maOoxData.maLocale = rLocale; + maOoxData.maFmtCode = OStringToOUString( OString( pcFmtCode ), RTL_TEXTENCODING_UTF8 ); + maOoxData.mnPredefId = -1; +} + +void NumberFormat::setPredefinedId( const Locale& rLocale, sal_Int16 nPredefId ) +{ + maOoxData.maLocale = rLocale; + maOoxData.maFmtCode = OUString(); + maOoxData.mnPredefId = nPredefId; +} + +void NumberFormat::finalizeImport( const Reference< XNumberFormats >& rxNumFmts, const Locale& rFromLocale ) +{ + if( rxNumFmts.is() && (maOoxData.maFmtCode.getLength() > 0) ) + maApiData.mnIndex = lclCreateFormat( rxNumFmts, maOoxData.maFmtCode, maOoxData.maLocale, rFromLocale ); + else + maApiData.mnIndex = lclCreatePredefinedFormat( rxNumFmts, maOoxData.mnPredefId, maOoxData.maLocale ); +} + +void NumberFormat::writeToPropertySet( PropertySet& rPropSet ) const +{ + getStylesPropertyHelper().writeNumFmtProperties( rPropSet, maApiData ); +} + +// ============================================================================ + +NumberFormatsBuffer::NumberFormatsBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnNextBiffIndex( 0 ) +{ + // get the current locale + try + { + Reference< XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory(); + Reference< XMultiServiceFactory > xConfigProv( + xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.configuration.ConfigurationProvider" ) ), + UNO_QUERY_THROW ); + + // try user-defined locale setting + Sequence< Any > aArgs( 1 ); + aArgs[ 0 ] <<= CREATE_OUSTRING( "org.openoffice.Setup/L10N/" ); + Reference< XNameAccess > xConfigNA( xConfigProv->createInstanceWithArguments( + CREATE_OUSTRING( "com.sun.star.configuration.ConfigurationAccess" ), aArgs ), UNO_QUERY_THROW ); + xConfigNA->getByName( CREATE_OUSTRING( "ooSetupSystemLocale" ) ) >>= maLocaleStr; + + // if set to "use system", get locale from system + if( maLocaleStr.getLength() == 0 ) + { + aArgs[ 0 ] <<= CREATE_OUSTRING( "org.openoffice.System/L10N/" ); + xConfigNA.set( xConfigProv->createInstanceWithArguments( + CREATE_OUSTRING( "com.sun.star.configuration.ConfigurationAccess" ), aArgs ), UNO_QUERY_THROW ); + xConfigNA->getByName( CREATE_OUSTRING( "Locale" ) ) >>= maLocaleStr; + } + } + catch( Exception& ) + { + OSL_ENSURE( false, "NumberFormatsBuffer::NumberFormatsBuffer - cannot get system locale" ); + } + + // create built-in formats for current locale + insertBuiltinFormats(); +} + +NumberFormatRef NumberFormatsBuffer::createNumFmt( sal_Int32 nNumFmtId, const OUString& rFmtCode ) +{ + NumberFormatRef xNumFmt; + if( nNumFmtId >= 0 ) + { + xNumFmt.reset( new NumberFormat( *this ) ); + maNumFmts[ nNumFmtId ] = xNumFmt; + xNumFmt->setFormatCode( rFmtCode ); + } + return xNumFmt; +} + +NumberFormatRef NumberFormatsBuffer::importNumFmt( const AttributeList& rAttribs ) +{ + sal_Int32 nNumFmtId = rAttribs.getInteger( XML_numFmtId, -1 ); + OUString aFmtCode = rAttribs.getString( XML_formatCode ); + return createNumFmt( nNumFmtId, aFmtCode ); +} + +void NumberFormatsBuffer::importNumFmt( RecordInputStream& rStrm ) +{ + sal_Int32 nNumFmtId = rStrm.readuInt16(); + OUString aFmtCode = rStrm.readString(); + createNumFmt( nNumFmtId, aFmtCode ); +} + +void NumberFormatsBuffer::importFormat( BiffInputStream& rStrm ) +{ + OUString aFmtCode; + switch( getBiff() ) + { + case BIFF2: + case BIFF3: + aFmtCode = rStrm.readByteString( false, getTextEncoding() ); + break; + case BIFF4: + // in BIFF4 the index field exists, but is undefined + aFmtCode = rStrm.skip( 2 ).readByteString( false, getTextEncoding() ); + break; + case BIFF5: + mnNextBiffIndex = rStrm.readuInt16(); + aFmtCode = rStrm.readByteString( false, getTextEncoding() ); + break; + case BIFF8: + mnNextBiffIndex = rStrm.readuInt16(); + aFmtCode = rStrm.readUniString(); + break; + case BIFF_UNKNOWN: break; + } + + createNumFmt( mnNextBiffIndex, aFmtCode ); + ++mnNextBiffIndex; +} + +void NumberFormatsBuffer::finalizeImport() +{ + maNumFmts.forEach( NumberFormatFunctor( *this ) ); +} + +void NumberFormatsBuffer::writeToPropertySet( PropertySet& rPropSet, sal_Int32 nNumFmtId ) const +{ + if( const NumberFormat* pNumFmt = maNumFmts.get( nNumFmtId ).get() ) + pNumFmt->writeToPropertySet( rPropSet ); +} + +void NumberFormatsBuffer::insertBuiltinFormats() +{ + // build a map containing pointers to all tables + typedef ::std::map< OUString, const BuiltinFormatTable* > BuiltinMap; + BuiltinMap aBuiltinMap; + for( const BuiltinFormatTable* pTable = spBuiltinFormatTables; + pTable != STATIC_ARRAY_END( spBuiltinFormatTables ); ++pTable ) + aBuiltinMap[ OUString::createFromAscii( pTable->mpcLocale ) ] = pTable; + + // convert locale string to locale struct + Locale aSysLocale; + sal_Int32 nDashPos = maLocaleStr.indexOf( '-' ); + if( nDashPos < 0 ) nDashPos = maLocaleStr.getLength(); + aSysLocale.Language = maLocaleStr.copy( 0, nDashPos ); + if( nDashPos + 1 < maLocaleStr.getLength() ) + aSysLocale.Country = maLocaleStr.copy( nDashPos + 1 ); + + // build a list of table pointers for the current locale, with all parent tables + typedef ::std::vector< const BuiltinFormatTable* > BuiltinVec; + BuiltinVec aBuiltinVec; + BuiltinMap::const_iterator aMIt = aBuiltinMap.find( maLocaleStr ), aMEnd = aBuiltinMap.end(); + OSL_ENSURE( aMIt != aMEnd, + OStringBuffer( "NumberFormatsBuffer::insertBuiltinFormats - locale '" ). + append( OUStringToOString( maLocaleStr, RTL_TEXTENCODING_ASCII_US ) ). + append( "' not supported (#i29949#)" ).getStr() ); + // start with default table, if no table has been found + if( aMIt == aMEnd ) + aMIt = aBuiltinMap.find( CREATE_OUSTRING( "*" ) ); + OSL_ENSURE( aMIt != aMEnd, "NumberFormatsBuffer::insertBuiltinFormats - default map not found" ); + // insert all tables into the vector + for( ; aMIt != aMEnd; aMIt = aBuiltinMap.find( OUString::createFromAscii( aMIt->second->mpcParent ) ) ) + aBuiltinVec.push_back( aMIt->second ); + + // insert the default formats in the format map (in reverse order from default table to system locale) + typedef ::std::map< sal_Int32, sal_Int32 > ReuseMap; + ReuseMap aReuseMap; + for( BuiltinVec::reverse_iterator aVIt = aBuiltinVec.rbegin(), aVEnd = aBuiltinVec.rend(); aVIt != aVEnd; ++aVIt ) + { + // do not put the current system locale for default table + Locale aLocale; + if( (*aVIt)->mpcLocale[ 0 ] != '\0' ) + aLocale = aSysLocale; + for( const BuiltinFormat* pBuiltin = (*aVIt)->mpFormats; pBuiltin && (pBuiltin->mnNumFmtId >= 0); ++pBuiltin ) + { + NumberFormatRef& rxNumFmt = maNumFmts[ pBuiltin->mnNumFmtId ]; + rxNumFmt.reset( new NumberFormat( *this ) ); + + bool bReuse = false; + if( pBuiltin->mpcFmtCode ) + rxNumFmt->setFormatCode( aLocale, pBuiltin->mpcFmtCode ); + else if( pBuiltin->mnPredefId >= 0 ) + rxNumFmt->setPredefinedId( aLocale, pBuiltin->mnPredefId ); + else + bReuse = pBuiltin->mnReuseId >= 0; + + if( bReuse ) + aReuseMap[ pBuiltin->mnNumFmtId ] = pBuiltin->mnReuseId; + else + aReuseMap.erase( pBuiltin->mnNumFmtId ); + } + } + + // copy reused number formats + for( ReuseMap::const_iterator aRIt = aReuseMap.begin(), aREnd = aReuseMap.end(); aRIt != aREnd; ++aRIt ) + maNumFmts[ aRIt->first ] = maNumFmts[ aRIt->second ]; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/pagesettings.cxx b/oox/source/xls/pagesettings.cxx new file mode 100644 index 000000000000..6cb32f961352 --- /dev/null +++ b/oox/source/xls/pagesettings.cxx @@ -0,0 +1,641 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pagesettings.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:08 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/pagesettings.hxx" +#include <algorithm> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/sheet/XHeaderFooterContent.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/ooxtokens.hxx" +#include "oox/xls/unitconverter.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::container::XNamed; +using ::com::sun::star::sheet::XHeaderFooterContent; +using ::com::sun::star::style::XStyle; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const double OOX_MARGIN_DEFAULT_LR = 0.748; /// Left/right default margin in inches. +const double OOX_MARGIN_DEFAULT_TB = 0.984; /// Top/bottom default margin in inches. +const double OOX_MARGIN_DEFAULT_HF = 0.512; /// Header/footer default margin in inches. + +const sal_uInt16 OOBIN_HEADERFOOTER_DIFFEVEN = 0x0001; +const sal_uInt16 OOBIN_HEADERFOOTER_DIFFFIRST = 0x0002; +const sal_uInt16 OOBIN_HEADERFOOTER_SCALEDOC = 0x0004; +const sal_uInt16 OOBIN_HEADERFOOTER_ALIGNMARGIN = 0x0008; + +const sal_uInt16 OOBIN_PAGESETUP_INROWS = 0x0001; +const sal_uInt16 OOBIN_PAGESETUP_LANDSCAPE = 0x0002; +const sal_uInt16 OOBIN_PAGESETUP_INVALID = 0x0004; +const sal_uInt16 OOBIN_PAGESETUP_BLACKWHITE = 0x0008; +const sal_uInt16 OOBIN_PAGESETUP_DRAFTQUALITY = 0x0010; +const sal_uInt16 OOBIN_PAGESETUP_PRINTNOTES = 0x0020; +const sal_uInt16 OOBIN_PAGESETUP_DEFAULTORIENT = 0x0040; +const sal_uInt16 OOBIN_PAGESETUP_USEFIRSTPAGE = 0x0080; +const sal_uInt16 OOBIN_PAGESETUP_NOTES_END = 0x0100; // different to BIFF flag + +const sal_uInt16 OOBIN_PRINTOPT_HORCENTER = 0x0001; +const sal_uInt16 OOBIN_PRINTOPT_VERCENTER = 0x0002; +const sal_uInt16 OOBIN_PRINTOPT_PRINTHEADING = 0x0004; +const sal_uInt16 OOBIN_PRINTOPT_PRINTGRID = 0x0008; + +const sal_uInt16 BIFF_PAGESETUP_INROWS = 0x0001; +const sal_uInt16 BIFF_PAGESETUP_PORTRAIT = 0x0002; +const sal_uInt16 BIFF_PAGESETUP_INVALID = 0x0004; +const sal_uInt16 BIFF_PAGESETUP_BLACKWHITE = 0x0008; +const sal_uInt16 BIFF_PAGESETUP_DRAFTQUALITY = 0x0010; +const sal_uInt16 BIFF_PAGESETUP_PRINTNOTES = 0x0020; +const sal_uInt16 BIFF_PAGESETUP_DEFAULTORIENT = 0x0040; +const sal_uInt16 BIFF_PAGESETUP_USEFIRSTPAGE = 0x0080; +const sal_uInt16 BIFF_PAGESETUP_NOTES_END = 0x0200; + +} // namespace + +// ============================================================================ + +OoxPageData::OoxPageData() : + mfLeftMargin( OOX_MARGIN_DEFAULT_LR ), + mfRightMargin( OOX_MARGIN_DEFAULT_LR ), + mfTopMargin( OOX_MARGIN_DEFAULT_TB ), + mfBottomMargin( OOX_MARGIN_DEFAULT_TB ), + mfHeaderMargin( OOX_MARGIN_DEFAULT_HF ), + mfFooterMargin( OOX_MARGIN_DEFAULT_HF ), + mnPaperSize( 1 ), + mnCopies( 1 ), + mnScale( 100 ), + mnFirstPage( 1 ), + mnFitToWidth( 1 ), + mnFitToHeight( 1 ), + mnHorPrintRes( 600 ), + mnVerPrintRes( 600 ), + mnOrientation( XML_default ), + mnPageOrder( XML_downThenOver ), + mnCellComments( XML_none ), + mnPrintErrors( XML_displayed ), + mbUseEvenHF( false ), + mbUseFirstHF( false ), + mbValidSettings( true ), + mbUseFirstPage( false ), + mbBlackWhite( false ), + mbDraftQuality( false ), + mbFitToPages( false ), + mbHorCenter( false ), + mbVerCenter( false ), + mbPrintGrid( false ), + mbPrintHeadings( false ) +{ +} + +void OoxPageData::setBinPrintErrors( sal_uInt8 nPrintErrors ) +{ + static const sal_Int32 spnErrorIds[] = { XML_displayed, XML_none, XML_dash, XML_NA }; + mnPrintErrors = STATIC_ARRAY_SELECT( spnErrorIds, nPrintErrors, XML_none ); +} + +// ============================================================================ + +PageSettings::PageSettings( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ) +{ +} + +void PageSettings::importPageMargins( const AttributeList& rAttribs ) +{ + maOoxData.mfLeftMargin = rAttribs.getDouble( XML_left, OOX_MARGIN_DEFAULT_LR ); + maOoxData.mfRightMargin = rAttribs.getDouble( XML_right, OOX_MARGIN_DEFAULT_LR ); + maOoxData.mfTopMargin = rAttribs.getDouble( XML_top, OOX_MARGIN_DEFAULT_TB ); + maOoxData.mfBottomMargin = rAttribs.getDouble( XML_bottom, OOX_MARGIN_DEFAULT_TB ); + maOoxData.mfHeaderMargin = rAttribs.getDouble( XML_header, OOX_MARGIN_DEFAULT_HF ); + maOoxData.mfFooterMargin = rAttribs.getDouble( XML_footer, OOX_MARGIN_DEFAULT_HF ); +} + +void PageSettings::importPageSetup( const AttributeList& rAttribs ) +{ + maOoxData.maRelId = rAttribs.getString( R_TOKEN( id ) ); + maOoxData.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 ); + maOoxData.mnCopies = rAttribs.getInteger( XML_copies, 1 ); + maOoxData.mnScale = rAttribs.getInteger( XML_scale, 100 ); + maOoxData.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 ); + maOoxData.mnFitToWidth = rAttribs.getInteger( XML_fitToWidth, 1 ); + maOoxData.mnFitToHeight = rAttribs.getInteger( XML_fitToHeight, 1 ); + maOoxData.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 ); + maOoxData.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 ); + maOoxData.mnOrientation = rAttribs.getToken( XML_orientation, XML_default ); + maOoxData.mnPageOrder = rAttribs.getToken( XML_pageOrder, XML_downThenOver ); + maOoxData.mnCellComments = rAttribs.getToken( XML_cellComments, XML_none ); + maOoxData.mnPrintErrors = rAttribs.getToken( XML_errors, XML_displayed ); + maOoxData.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, true ); + maOoxData.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false ); + maOoxData.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false ); + maOoxData.mbDraftQuality = rAttribs.getBool( XML_draft, false ); +} + +void PageSettings::importPrintOptions( const AttributeList& rAttribs ) +{ + maOoxData.mbHorCenter = rAttribs.getBool( XML_horizontalCentered, false ); + maOoxData.mbVerCenter = rAttribs.getBool( XML_verticalCentered, false ); + maOoxData.mbPrintGrid = rAttribs.getBool( XML_gridLines, false ); + maOoxData.mbPrintHeadings = rAttribs.getBool( XML_headings, false ); +} + +void PageSettings::importHeaderFooter( const AttributeList& rAttribs ) +{ + maOoxData.mbUseEvenHF = rAttribs.getBool( XML_differentOddEven, false ); + maOoxData.mbUseFirstHF = rAttribs.getBool( XML_differentFirst, false ); +} + +void PageSettings::importHeaderFooterCharacters( const OUString& rChars, sal_Int32 nElement ) +{ + switch( nElement ) + { + case XLS_TOKEN( oddHeader ): maOoxData.maOddHeader += rChars; break; + case XLS_TOKEN( oddFooter ): maOoxData.maOddFooter += rChars; break; + case XLS_TOKEN( evenHeader ): maOoxData.maEvenHeader += rChars; break; + case XLS_TOKEN( evenFooter ): maOoxData.maEvenFooter += rChars; break; + case XLS_TOKEN( firstHeader ): maOoxData.maFirstHeader += rChars; break; + case XLS_TOKEN( firstFooter ): maOoxData.maFirstFooter += rChars; break; + } +} + +void PageSettings::importPageMargins( RecordInputStream& rStrm ) +{ + rStrm >> maOoxData.mfLeftMargin >> maOoxData.mfRightMargin + >> maOoxData.mfTopMargin >> maOoxData.mfBottomMargin + >> maOoxData.mfHeaderMargin >> maOoxData.mfFooterMargin; +} + +void PageSettings::importPageSetup( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> maOoxData.mnPaperSize >> maOoxData.mnScale + >> maOoxData.mnHorPrintRes >> maOoxData.mnVerPrintRes + >> maOoxData.mnCopies >> maOoxData.mnFirstPage + >> maOoxData.mnFitToWidth >> maOoxData.mnFitToHeight + >> nFlags >> maOoxData.maRelId; + maOoxData.setBinPrintErrors( extractValue< sal_uInt8 >( nFlags, 9, 2 ) ); + maOoxData.mnOrientation = getFlagValue( nFlags, OOBIN_PAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, OOBIN_PAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) ); + maOoxData.mnPageOrder = getFlagValue( nFlags, OOBIN_PAGESETUP_INROWS, XML_overThenDown, XML_downThenOver ); + maOoxData.mnCellComments = getFlagValue( nFlags, OOBIN_PAGESETUP_PRINTNOTES, getFlagValue( nFlags, OOBIN_PAGESETUP_NOTES_END, XML_atEnd, XML_asDisplayed ), XML_none ); + maOoxData.mbValidSettings = !getFlag( nFlags, OOBIN_PAGESETUP_INVALID ); + maOoxData.mbUseFirstPage = getFlag( nFlags, OOBIN_PAGESETUP_USEFIRSTPAGE ); + maOoxData.mbBlackWhite = getFlag( nFlags, OOBIN_PAGESETUP_BLACKWHITE ); + maOoxData.mbDraftQuality = getFlag( nFlags, OOBIN_PAGESETUP_DRAFTQUALITY ); +} + +void PageSettings::importPrintOptions( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags; + maOoxData.mbHorCenter = getFlag( nFlags, OOBIN_PRINTOPT_HORCENTER ); + maOoxData.mbVerCenter = getFlag( nFlags, OOBIN_PRINTOPT_VERCENTER ); + maOoxData.mbPrintGrid = getFlag( nFlags, OOBIN_PRINTOPT_PRINTGRID ); + maOoxData.mbPrintHeadings = getFlag( nFlags, OOBIN_PRINTOPT_PRINTHEADING ); +} + +void PageSettings::importHeaderFooter( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags + >> maOoxData.maOddHeader >> maOoxData.maOddFooter + >> maOoxData.maEvenHeader >> maOoxData.maEvenFooter + >> maOoxData.maFirstHeader >> maOoxData.maFirstFooter; + maOoxData.mbUseEvenHF = getFlag( nFlags, OOBIN_HEADERFOOTER_DIFFEVEN ); + maOoxData.mbUseFirstHF = getFlag( nFlags, OOBIN_HEADERFOOTER_DIFFFIRST ); +} + +void PageSettings::importLeftMargin( BiffInputStream& rStrm ) +{ + rStrm >> maOoxData.mfLeftMargin; +} + +void PageSettings::importRightMargin( BiffInputStream& rStrm ) +{ + rStrm >> maOoxData.mfRightMargin; +} + +void PageSettings::importTopMargin( BiffInputStream& rStrm ) +{ + rStrm >> maOoxData.mfTopMargin; +} + +void PageSettings::importBottomMargin( BiffInputStream& rStrm ) +{ + rStrm >> maOoxData.mfBottomMargin; +} + +void PageSettings::importPageSetup( BiffInputStream& rStrm ) +{ + sal_uInt16 nPaperSize, nScale, nFirstPage, nFitToWidth, nFitToHeight, nFlags; + rStrm >> nPaperSize >> nScale >> nFirstPage >> nFitToWidth >> nFitToHeight >> nFlags; + + maOoxData.mnPaperSize = nPaperSize; // equal in BIFF and OOX + maOoxData.mnScale = nScale; + maOoxData.mnFirstPage = nFirstPage; + maOoxData.mnFitToWidth = nFitToWidth; + maOoxData.mnFitToHeight = nFitToHeight; + maOoxData.mnOrientation = getFlagValue( nFlags, BIFF_PAGESETUP_PORTRAIT, XML_portrait, XML_landscape ); + maOoxData.mnPageOrder = getFlagValue( nFlags, BIFF_PAGESETUP_INROWS, XML_overThenDown, XML_downThenOver ); + maOoxData.mbValidSettings = !getFlag( nFlags, BIFF_PAGESETUP_INVALID ); + maOoxData.mbUseFirstPage = true; + maOoxData.mbBlackWhite = getFlag( nFlags, BIFF_PAGESETUP_BLACKWHITE ); + + if( getBiff() >= BIFF5 ) + { + sal_uInt16 nHorPrintRes, nVerPrintRes, nCopies; + rStrm >> nHorPrintRes >> nVerPrintRes >> maOoxData.mfHeaderMargin >> maOoxData.mfFooterMargin >> nCopies; + + maOoxData.mnCopies = nCopies; + maOoxData.mnOrientation = getFlagValue( nFlags, BIFF_PAGESETUP_DEFAULTORIENT, XML_default, maOoxData.mnOrientation ); + maOoxData.mnHorPrintRes = nHorPrintRes; + maOoxData.mnVerPrintRes = nVerPrintRes; + maOoxData.mnCellComments = getFlagValue( nFlags, BIFF_PAGESETUP_PRINTNOTES, XML_asDisplayed, XML_none ); + maOoxData.mbUseFirstPage = getFlag( nFlags, BIFF_PAGESETUP_USEFIRSTPAGE ); + maOoxData.mbDraftQuality = getFlag( nFlags, BIFF_PAGESETUP_DRAFTQUALITY ); + + if( getBiff() == BIFF8 ) + { + maOoxData.setBinPrintErrors( extractValue< sal_uInt8 >( nFlags, 10, 2 ) ); + maOoxData.mnCellComments = getFlagValue( nFlags, BIFF_PAGESETUP_PRINTNOTES, getFlagValue( nFlags, BIFF_PAGESETUP_NOTES_END, XML_atEnd, XML_asDisplayed ), XML_none ); + } + } +} + +void PageSettings::importHorCenter( BiffInputStream& rStrm ) +{ + maOoxData.mbHorCenter = rStrm.readuInt16() != 0; +} + +void PageSettings::importVerCenter( BiffInputStream& rStrm ) +{ + maOoxData.mbVerCenter = rStrm.readuInt16() != 0; +} + +void PageSettings::importPrintHeaders( BiffInputStream& rStrm ) +{ + maOoxData.mbPrintHeadings = rStrm.readuInt16() != 0; +} + +void PageSettings::importPrintGridLines( BiffInputStream& rStrm ) +{ + maOoxData.mbPrintGrid = rStrm.readuInt16() != 0; +} + +void PageSettings::importHeader( BiffInputStream& rStrm ) +{ + if( rStrm.getRecLeft() > 0 ) + maOoxData.maOddHeader = (getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteString( false, getTextEncoding() ); + else + maOoxData.maOddHeader = OUString(); +} + +void PageSettings::importFooter( BiffInputStream& rStrm ) +{ + if( rStrm.getRecLeft() > 0 ) + maOoxData.maOddFooter = (getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteString( false, getTextEncoding() ); + else + maOoxData.maOddFooter = OUString(); +} + +void PageSettings::setFitToPagesMode( bool bFitToPages ) +{ + maOoxData.mbFitToPages = bFitToPages; +} + +void PageSettings::finalizeImport() +{ + OUStringBuffer aStyleNameBuffer( CREATE_OUSTRING( "PageStyle_" ) ); + Reference< XNamed > xSheetName( getXSpreadsheet(), UNO_QUERY ); + if( xSheetName.is() ) + aStyleNameBuffer.append( xSheetName->getName() ); + else + aStyleNameBuffer.append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) ); + OUString aStyleName = aStyleNameBuffer.makeStringAndClear(); + + Reference< XStyle > xStyle = createStyleObject( aStyleName, true ); + PropertySet aStyleProps( xStyle ); + getPageSettingsPropertyHelper().writePageSettingsProperties( aStyleProps, maOoxData, getSheetType() ); + + PropertySet aSheetProps( getXSpreadsheet() ); + aSheetProps.setProperty( CREATE_OUSTRING( "PageStyle" ), aStyleName ); +} + +// ============================================================================ + +namespace { + +/** Property names for page style settings. */ +const sal_Char* const sppcPageNames[] = +{ + "IsLandscape", + "FirstPageNumber", + "PrintDownFirst", + "PrintAnnotations", + "CenterHorizontally", + "CenterVertically", + "PrintGrid", + "PrintHeaders", + "LeftMargin", + "RightMargin", + "TopMargin", + "BottomMargin", + "HeaderIsOn", + "HeaderIsShared", + "HeaderIsDynamicHeight", + "HeaderHeight", + "HeaderBodyDistance", + "FooterIsOn", + "FooterIsShared", + "FooterIsDynamicHeight", + "FooterHeight", + "FooterBodyDistance", + 0 +}; + +/** Paper size in 1/100 millimeters. */ +struct ApiPaperSize +{ + sal_Int32 mnWidth; + sal_Int32 mnHeight; +}; + +#define IN2MM100( v ) static_cast< sal_Int32 >( (v) * 2540.0 + 0.5 ) +#define MM2MM100( v ) static_cast< sal_Int32 >( (v) * 100.0 + 0.5 ) + +static const ApiPaperSize spPaperSizeTable[] = +{ + { 0, 0 }, // 0 - (undefined) + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 1 - Letter paper + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 2 - Letter small paper + { IN2MM100( 11 ), IN2MM100( 17 ) }, // 3 - Tabloid paper + { IN2MM100( 17 ), IN2MM100( 11 ) }, // 4 - Ledger paper + { IN2MM100( 8.5 ), IN2MM100( 14 ) }, // 5 - Legal paper + { IN2MM100( 5.5 ), IN2MM100( 8.5 ) }, // 6 - Statement paper + { IN2MM100( 7.25 ), IN2MM100( 10.5 ) }, // 7 - Executive paper + { MM2MM100( 297 ), MM2MM100( 420 ) }, // 8 - A3 paper + { MM2MM100( 210 ), MM2MM100( 297 ) }, // 9 - A4 paper + { MM2MM100( 210 ), MM2MM100( 297 ) }, // 10 - A4 small paper + { MM2MM100( 148 ), MM2MM100( 210 ) }, // 11 - A5 paper + { MM2MM100( 250 ), MM2MM100( 353 ) }, // 12 - B4 paper + { MM2MM100( 176 ), MM2MM100( 250 ) }, // 13 - B5 paper + { IN2MM100( 8.5 ), IN2MM100( 13 ) }, // 14 - Folio paper + { MM2MM100( 215 ), MM2MM100( 275 ) }, // 15 - Quarto paper + { IN2MM100( 10 ), IN2MM100( 14 ) }, // 16 - Standard paper + { IN2MM100( 11 ), IN2MM100( 17 ) }, // 17 - Standard paper + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 18 - Note paper + { IN2MM100( 3.875 ), IN2MM100( 8.875 ) }, // 19 - #9 envelope + { IN2MM100( 4.125 ), IN2MM100( 9.5 ) }, // 20 - #10 envelope + { IN2MM100( 4.5 ), IN2MM100( 10.375 ) }, // 21 - #11 envelope + { IN2MM100( 4.75 ), IN2MM100( 11 ) }, // 22 - #12 envelope + { IN2MM100( 5 ), IN2MM100( 11.5 ) }, // 23 - #14 envelope + { IN2MM100( 17 ), IN2MM100( 22 ) }, // 24 - C paper + { IN2MM100( 22 ), IN2MM100( 34 ) }, // 25 - D paper + { IN2MM100( 34 ), IN2MM100( 44 ) }, // 26 - E paper + { MM2MM100( 110 ), MM2MM100( 220 ) }, // 27 - DL envelope + { MM2MM100( 162 ), MM2MM100( 229 ) }, // 28 - C5 envelope + { MM2MM100( 324 ), MM2MM100( 458 ) }, // 29 - C3 envelope + { MM2MM100( 229 ), MM2MM100( 324 ) }, // 30 - C4 envelope + { MM2MM100( 114 ), MM2MM100( 162 ) }, // 31 - C6 envelope + { MM2MM100( 114 ), MM2MM100( 229 ) }, // 32 - C65 envelope + { MM2MM100( 250 ), MM2MM100( 353 ) }, // 33 - B4 envelope + { MM2MM100( 176 ), MM2MM100( 250 ) }, // 34 - B5 envelope + { MM2MM100( 176 ), MM2MM100( 125 ) }, // 35 - B6 envelope + { MM2MM100( 110 ), MM2MM100( 230 ) }, // 36 - Italy envelope + { IN2MM100( 3.875 ), IN2MM100( 7.5 ) }, // 37 - Monarch envelope + { IN2MM100( 3.625 ), IN2MM100( 6.5 ) }, // 38 - 6 3/4 envelope + { IN2MM100( 14.875 ), IN2MM100( 11 ) }, // 39 - US standard fanfold + { IN2MM100( 8.5 ), IN2MM100( 12 ) }, // 40 - German standard fanfold + { IN2MM100( 8.5 ), IN2MM100( 13 ) }, // 41 - German legal fanfold + { MM2MM100( 250 ), MM2MM100( 353 ) }, // 42 - ISO B4 + { MM2MM100( 200 ), MM2MM100( 148 ) }, // 43 - Japanese double postcard + { IN2MM100( 9 ), IN2MM100( 11 ) }, // 44 - Standard paper + { IN2MM100( 10 ), IN2MM100( 11 ) }, // 45 - Standard paper + { IN2MM100( 15 ), IN2MM100( 11 ) }, // 46 - Standard paper + { MM2MM100( 220 ), MM2MM100( 220 ) }, // 47 - Invite envelope + { 0, 0 }, // 48 - (undefined) + { 0, 0 }, // 49 - (undefined) + { IN2MM100( 9.275 ), IN2MM100( 12 ) }, // 50 - Letter extra paper + { IN2MM100( 9.275 ), IN2MM100( 15 ) }, // 51 - Legal extra paper + { IN2MM100( 11.69 ), IN2MM100( 18 ) }, // 52 - Tabloid extra paper + { MM2MM100( 236 ), MM2MM100( 322 ) }, // 53 - A4 extra paper + { IN2MM100( 8.275 ), IN2MM100( 11 ) }, // 54 - Letter transverse paper + { MM2MM100( 210 ), MM2MM100( 297 ) }, // 55 - A4 transverse paper + { IN2MM100( 9.275 ), IN2MM100( 12 ) }, // 56 - Letter extra transverse paper + { MM2MM100( 227 ), MM2MM100( 356 ) }, // 57 - SuperA/SuperA/A4 paper + { MM2MM100( 305 ), MM2MM100( 487 ) }, // 58 - SuperB/SuperB/A3 paper + { IN2MM100( 8.5 ), IN2MM100( 12.69 ) }, // 59 - Letter plus paper + { MM2MM100( 210 ), MM2MM100( 330 ) }, // 60 - A4 plus paper + { MM2MM100( 148 ), MM2MM100( 210 ) }, // 61 - A5 transverse paper + { MM2MM100( 182 ), MM2MM100( 257 ) }, // 62 - JIS B5 transverse paper + { MM2MM100( 322 ), MM2MM100( 445 ) }, // 63 - A3 extra paper + { MM2MM100( 174 ), MM2MM100( 235 ) }, // 64 - A5 extra paper + { MM2MM100( 201 ), MM2MM100( 276 ) }, // 65 - ISO B5 extra paper + { MM2MM100( 420 ), MM2MM100( 594 ) }, // 66 - A2 paper + { MM2MM100( 297 ), MM2MM100( 420 ) }, // 67 - A3 transverse paper + { MM2MM100( 322 ), MM2MM100( 445 ) } // 68 - A3 extra transverse paper +}; + +} // namespace + +// ---------------------------------------------------------------------------- + +PageSettingsPropertyHelper::HFHelperData::HFHelperData( const OUString& rLeftProp, const OUString& rRightProp ) : + maLeftProp( rLeftProp ), + maRightProp( rRightProp ), + mnHeight( 0 ), + mnBodyDist( 0 ), + mbHasContent( false ), + mbShareOddEven( false ), + mbDynamicHeight( false ) +{ +} + +// ---------------------------------------------------------------------------- + +PageSettingsPropertyHelper::PageSettingsPropertyHelper( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maHFParser( rHelper ), + maPageProps( sppcPageNames ), + maHeaderData( CREATE_OUSTRING( "LeftPageHeaderContent" ), CREATE_OUSTRING( "RightPageHeaderContent" ) ), + maFooterData( CREATE_OUSTRING( "LeftPageFooterContent" ), CREATE_OUSTRING( "RightPageFooterContent" ) ) +{ +} + +void PageSettingsPropertyHelper::writePageSettingsProperties( + PropertySet& rPropSet, const OoxPageData& rData, WorksheetType eSheetType ) +{ + // printout scaling + if( rData.mbFitToPages ) + { + // fit to number of pages + rPropSet.setProperty( CREATE_OUSTRING( "ScaleToPagesX" ), getLimitedValue< sal_Int16, sal_Int32 >( rData.mnFitToWidth, 0, 1000 ) ); + rPropSet.setProperty( CREATE_OUSTRING( "ScaleToPagesY" ), getLimitedValue< sal_Int16, sal_Int32 >( rData.mnFitToHeight, 0, 1000 ) ); + } + else + { + // scale may be 0 which indicates uninitialized + sal_Int16 nScale = (rData.mbValidSettings && (rData.mnScale > 0)) ? getLimitedValue< sal_Int16, sal_Int32 >( rData.mnScale, 10, 400 ) : 100; + rPropSet.setProperty( CREATE_OUSTRING( "PageScale" ), nScale ); + } + + // paper orientation + bool bLandscape = rData.mnOrientation == XML_landscape; + // default orientation for current sheet type (chart sheets default to landscape) + if( !rData.mbValidSettings || (rData.mnOrientation == XML_default) ) switch( eSheetType ) + { + case SHEETTYPE_WORKSHEET: bLandscape = false; break; + case SHEETTYPE_CHART: bLandscape = true; break; + case SHEETTYPE_MACRO: bLandscape = false; break; + } + + // paper size + if( rData.mbValidSettings && (0 < rData.mnPaperSize) && (rData.mnPaperSize < static_cast< sal_Int32 >( STATIC_ARRAY_SIZE( spPaperSizeTable ) )) ) + { + const ApiPaperSize& rPaperSize = spPaperSizeTable[ rData.mnPaperSize ]; + ::com::sun::star::awt::Size aSize( rPaperSize.mnWidth, rPaperSize.mnHeight ); + if( bLandscape ) + ::std::swap( aSize.Width, aSize.Height ); + rPropSet.setProperty( CREATE_OUSTRING( "Size" ), aSize ); + } + + // header/footer + convertHeaderFooterData( rPropSet, maHeaderData, rData.maOddHeader, rData.maEvenHeader, rData.mbUseEvenHF, rData.mfTopMargin, rData.mfHeaderMargin ); + convertHeaderFooterData( rPropSet, maFooterData, rData.maOddFooter, rData.maEvenFooter, rData.mbUseEvenHF, rData.mfBottomMargin, rData.mfFooterMargin ); + + // write all properties to property set + const UnitConverter& rUnitConv = getUnitConverter(); + maPageProps + << bLandscape + << getLimitedValue< sal_Int16, sal_Int32 >( rData.mbUseFirstPage ? rData.mnFirstPage : 0, 0, 9999 ) + << (rData.mnPageOrder == XML_downThenOver) + << (rData.mnCellComments == XML_asDisplayed) + << rData.mbHorCenter + << rData.mbVerCenter + << rData.mbPrintGrid + << rData.mbPrintHeadings + << rUnitConv.calcMm100FromInches( rData.mfLeftMargin ) + << rUnitConv.calcMm100FromInches( rData.mfRightMargin ) + // #i23296# In Calc, "TopMargin" property is distance to top of header if enabled + << rUnitConv.calcMm100FromInches( maHeaderData.mbHasContent ? rData.mfHeaderMargin : rData.mfTopMargin ) + // #i23296# In Calc, "BottomMargin" property is distance to bottom of footer if enabled + << rUnitConv.calcMm100FromInches( maFooterData.mbHasContent ? rData.mfFooterMargin : rData.mfBottomMargin ) + << maHeaderData.mbHasContent + << maHeaderData.mbShareOddEven + << maHeaderData.mbDynamicHeight + << maHeaderData.mnHeight + << maHeaderData.mnBodyDist + << maFooterData.mbHasContent + << maFooterData.mbShareOddEven + << maFooterData.mbDynamicHeight + << maFooterData.mnHeight + << maFooterData.mnBodyDist + >> rPropSet; +} + +void PageSettingsPropertyHelper::convertHeaderFooterData( + PropertySet& rPropSet, HFHelperData& rHFData, + const OUString rOddContent, const OUString rEvenContent, bool bUseEvenContent, + double fPageMargin, double fContentMargin ) +{ + bool bHasOddContent = rOddContent.getLength() > 0; + bool bHasEvenContent = bUseEvenContent && (rEvenContent.getLength() > 0); + + sal_Int32 nOddHeight = bHasOddContent ? writeHeaderFooter( rPropSet, rHFData.maRightProp, rOddContent ) : 0; + sal_Int32 nEvenHeight = bHasEvenContent ? writeHeaderFooter( rPropSet, rHFData.maLeftProp, rEvenContent ) : 0; + + rHFData.mnHeight = 750; + rHFData.mnBodyDist = 250; + rHFData.mbHasContent = bHasOddContent || bHasEvenContent; + rHFData.mbShareOddEven = !bUseEvenContent; + rHFData.mbDynamicHeight = true; + + if( rHFData.mbHasContent ) + { + // use maximum height of odd/even header/footer + rHFData.mnHeight = ::std::max( nOddHeight, nEvenHeight ); + /* Calc contains distance between bottom of header and top of page + body in "HeaderBodyDistance" property, and distance between bottom + of page body and top of footer in "FooterBodyDistance" property */ + rHFData.mnBodyDist = getUnitConverter().calcMm100FromInches( fPageMargin - fContentMargin ) - rHFData.mnHeight; + /* #i23296# Distance less than 0 means, header or footer overlays page + body. As this is not possible in Calc, set fixed header or footer + height (crop header/footer) to get correct top position of page body. */ + rHFData.mbDynamicHeight = rHFData.mnBodyDist >= 0; + /* "HeaderHeight" property is in fact distance from top of header to + top of page body (including "HeaderBodyDistance"). + "FooterHeight" property is in fact distance from bottom of page + body to bottom of footer (including "FooterBodyDistance"). */ + rHFData.mnHeight += rHFData.mnBodyDist; + // negative body distance not allowed + rHFData.mnBodyDist = ::std::max< sal_Int32 >( rHFData.mnBodyDist, 0 ); + } +} + +sal_Int32 PageSettingsPropertyHelper::writeHeaderFooter( + PropertySet& rPropSet, const OUString& rPropName, const OUString& rContent ) +{ + OSL_ENSURE( rContent.getLength() > 0, "PageSettingsPropertyHelper::writeHeaderFooter - empty h/f string found" ); + sal_Int32 nHeight = 0; + if( rContent.getLength() > 0 ) + { + Reference< XHeaderFooterContent > xHFContent; + if( rPropSet.getProperty( xHFContent, rPropName ) && xHFContent.is() ) + { + maHFParser.parse( xHFContent, rContent ); + rPropSet.setProperty( rPropName, xHFContent ); + nHeight = getUnitConverter().calcMm100FromPoints( maHFParser.getTotalHeight() ); + } + } + return nHeight; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/pivotcachefragment.cxx b/oox/source/xls/pivotcachefragment.cxx new file mode 100644 index 000000000000..adc7fe2076cb --- /dev/null +++ b/oox/source/xls/pivotcachefragment.cxx @@ -0,0 +1,160 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pivotcachefragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/pivotcachefragment.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/xls/addressconverter.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::xml::sax::SAXException; + +namespace oox { +namespace xls { + +OoxPivotCacheFragment::OoxPivotCacheFragment( const WorkbookHelper& rHelper, + const OUString& rFragmentPath, + sal_uInt32 nCacheId ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ), + mnCacheId( nCacheId ), + mbValidSource( false ) +{ +} + +bool OoxPivotCacheFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: return nElement == XLS_TOKEN( pivotCacheDefinition ); + case XLS_TOKEN( pivotCacheDefinition ): return (nElement == XLS_TOKEN( cacheSource ) || + nElement == XLS_TOKEN( cacheFields )); + case XLS_TOKEN( cacheSource ): return nElement == XLS_TOKEN( worksheetSource ); + case XLS_TOKEN( cacheFields ): return nElement == XLS_TOKEN( cacheField ); + case XLS_TOKEN( cacheField ): return nElement == XLS_TOKEN( sharedItems ); + case XLS_TOKEN( sharedItems ): return nElement == XLS_TOKEN( s ); + } + return false; +} + +void OoxPivotCacheFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch ( getCurrentContext() ) + { + case XLS_TOKEN( pivotCacheDefinition ): + importPivotCacheDefinition( rAttribs ); + break; + case XLS_TOKEN( cacheSource ): + importCacheSource( rAttribs ); + break; + case XLS_TOKEN( worksheetSource ): + if ( mbValidSource ) + importWorksheetSource( rAttribs ); + break; + case XLS_TOKEN( cacheFields ): + if ( mbValidSource ) + maPCacheData.maFields.reserve( rAttribs.getUnsignedInteger(XML_count, 1) ); + break; + case XLS_TOKEN( cacheField ): + if ( mbValidSource ) + importCacheField( rAttribs ); + break; + case XLS_TOKEN( sharedItems ): + if ( mbValidSource ) + maPCacheData.maFields.back().maItems.reserve( rAttribs.getUnsignedInteger(XML_count, 1) ); + break; + case XLS_TOKEN( s ): + if ( mbValidSource ) + maPCacheData.maFields.back().maItems.push_back( rAttribs.getString( XML_v ) ); + break; + } +} + +void OoxPivotCacheFragment::finalizeImport() +{ + if( mbValidSource ) + getPivotTables().setPivotCache( mnCacheId, maPCacheData ); +} + +void OoxPivotCacheFragment::importPivotCacheDefinition( const AttributeList& /*rAttribs*/ ) +{ +} + +void OoxPivotCacheFragment::importCacheSource( const AttributeList& rAttribs ) +{ + switch ( rAttribs.getToken(XML_type) ) + { + case XML_worksheet: + maPCacheData.meSourceType = PivotCacheData::WORKSHEET; + maPCacheData.mpSourceProp.reset( new PivotCacheData::WorksheetSource ); + mbValidSource = true; + break; + case XML_external: + maPCacheData.meSourceType = PivotCacheData::EXTERNAL; + maPCacheData.mpSourceProp.reset( new PivotCacheData::ExternalSource ); + mbValidSource = true; + break; + default: + // unsupported case source type. + break; + } +} + +void OoxPivotCacheFragment::importWorksheetSource( const AttributeList& rAttribs ) +{ + if ( maPCacheData.meSourceType != PivotCacheData::WORKSHEET ) + return; + + PivotCacheData::WorksheetSource* pSrc = static_cast<PivotCacheData::WorksheetSource*>( + maPCacheData.mpSourceProp.get() ); + + pSrc->maSrcRange = rAttribs.getString( XML_ref ); + pSrc->maSheetName = rAttribs.getString( XML_sheet ); +} + +void OoxPivotCacheFragment::importCacheField( const AttributeList& rAttribs ) +{ + PivotCacheField aField; + aField.maName = rAttribs.getString( XML_name ); + maPCacheData.maFields.push_back(aField); +} + +} // namespace xls +} // namespace oox diff --git a/oox/source/xls/pivottablebuffer.cxx b/oox/source/xls/pivottablebuffer.cxx new file mode 100644 index 000000000000..08e61b8a16d5 --- /dev/null +++ b/oox/source/xls/pivottablebuffer.cxx @@ -0,0 +1,275 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pivottablebuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2007 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/pivottablebuffer.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/worksheetbuffer.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XElementAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/GeneralFunction.hpp> +#include <com/sun/star/sheet/XCellRangeData.hpp> +#include <com/sun/star/sheet/XDataPilotDescriptor.hpp> +#include <com/sun/star/sheet/XDataPilotField.hpp> +#include <com/sun/star/sheet/XDataPilotTables.hpp> +#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp> +#include <com/sun/star/sheet/XSheetFilterDescriptor.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/table/XCellRange.hpp> + +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNamed; +using ::com::sun::star::sheet::XCellRangeData; +using ::com::sun::star::sheet::XDataPilotDescriptor; +using ::com::sun::star::sheet::XDataPilotField; +using ::com::sun::star::sheet::XDataPilotTables; +using ::com::sun::star::sheet::XDataPilotTablesSupplier; +using ::com::sun::star::sheet::XSheetFilterDescriptor; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::table::XCellRange; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::rtl::OUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +PivotCacheData::PivotCacheData() : + meSourceType( WORKSHEET ), + mpSourceProp( static_cast<BaseSource*>(NULL) ) +{ +} + +PivotCacheData::WorksheetSource* PivotCacheData::getWorksheetSource() const +{ + if ( meSourceType != WORKSHEET ) + return NULL; + return static_cast<WorksheetSource*>( mpSourceProp.get() ); +} + +PivotCacheData::ExternalSource* PivotCacheData::getExternalSource() const +{ + if ( meSourceType != EXTERNAL ) + return NULL; + return static_cast<ExternalSource*>( mpSourceProp.get() ); +} + +// ============================================================================ + +PivotTableField::PivotTableField() : + meAxis( ROW ), + mbDataField( false ) +{ +} + +// ---------------------------------------------------------------------------- + +PivotTableData::PivotTableData() +{ +} + +// ---------------------------------------------------------------------------- + +PivotTableBuffer::PivotTableBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +const PivotCacheData* PivotTableBuffer::getPivotCache( sal_uInt32 nCacheId ) const +{ + PivotCacheMapType::const_iterator itr = maPivotCacheMap.find(nCacheId), + itrEnd = maPivotCacheMap.end(); + + if ( itr != itrEnd ) + return &itr->second; + + return NULL; +} + +void PivotTableBuffer::setPivotCache( sal_uInt32 nCacheId, const PivotCacheData& aData ) +{ + maPivotCacheMap.insert( PivotCacheMapType::value_type(nCacheId, aData) ); +} + +PivotTableData* PivotTableBuffer::getPivotTable( const OUString& aName ) +{ + PivotTableMapType::iterator itr = maPivotTableMap.find(aName), + itrEnd = maPivotTableMap.end(); + + if ( itr != itrEnd ) + return &itr->second; + + return NULL; +} + +void PivotTableBuffer::setPivotTable( const OUString& aName, const PivotTableData& aData ) +{ + maPivotTableMap.insert( PivotTableMapType::value_type(aName, aData) ); + maCellRangeMap.addCellRange( aData.maRange ); +} + +bool PivotTableBuffer::isOverlapping( const CellAddress& aCellAddress ) const +{ + return maCellRangeMap.isOverlapping(aCellAddress); +} + +void PivotTableBuffer::finalizeImport() const +{ + PivotTableMapType::const_iterator itr = maPivotTableMap.begin(), itrEnd = maPivotTableMap.end(); + for ( ; itr != itrEnd; ++itr ) + writePivotTable( itr->first, itr->second ); +} + +void PivotTableBuffer::writePivotTable( const OUString& aName, const PivotTableData& aData ) const +{ + using namespace ::com::sun::star::sheet; + + const PivotCacheData* pCache = getPivotCache( aData.mnCacheId ); + if ( !pCache ) + { + OSL_ENSURE( false, "OoxPivotTableFragment::commit: pivot cache data not found" ); + return; + } + + const CellRangeAddress& aRange = aData.maRange; + Reference< XSpreadsheet > xSheet = getSheet( aRange.Sheet ); + if ( !xSheet.is() ) + return; + + try + { + Reference< XDataPilotTablesSupplier > xDPTSupplier( xSheet, UNO_QUERY_THROW ); + + Reference< XDataPilotTables > xDPTables( xDPTSupplier->getDataPilotTables(), UNO_QUERY_THROW ); + Reference< XDataPilotDescriptor > xDPDesc( xDPTables->createDataPilotDescriptor(), UNO_QUERY_THROW ); + if ( pCache->meSourceType != PivotCacheData::WORKSHEET ) + return; + + PivotCacheData::WorksheetSource* pSrc = pCache->getWorksheetSource(); + if ( !pSrc ) + return; + + OUString sheetname = pSrc->maSheetName; + OUString srcrange = pSrc->maSrcRange; + + CellRangeAddress aSrcRange; + if ( !getSourceRange( pSrc->maSheetName, pSrc->maSrcRange, aSrcRange ) ) + return; + + xDPDesc->setSourceRange(aSrcRange); + Reference< XIndexAccess > xIA = xDPDesc->getDataPilotFields(); + + bool bPageAxisExists = false; + + // Go through all fields in pivot table, and register them. + sal_Int32 nCount = ::std::min( xIA->getCount(), static_cast<sal_Int32>(aData.maFields.size()) ); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + Reference< XDataPilotField > xField( xIA->getByIndex(i), UNO_QUERY_THROW ); + PropertySet aProp( xField ); + Reference< XNamed > xNamed( xField, UNO_QUERY_THROW ); + + PivotTableField::AxisType eAxis = aData.maFields[i].meAxis; + if ( aData.maFields[i].mbDataField ) + eAxis = PivotTableField::VALUES; + + switch ( eAxis ) + { + case PivotTableField::COLUMN: + aProp.setProperty( CREATE_OUSTRING("Orientation"), DataPilotFieldOrientation_COLUMN ); + break; + case PivotTableField::ROW: + aProp.setProperty( CREATE_OUSTRING("Orientation"), DataPilotFieldOrientation_ROW ); + break; + case PivotTableField::PAGE: + bPageAxisExists = true; + aProp.setProperty( CREATE_OUSTRING("Orientation"), DataPilotFieldOrientation_PAGE ); + break; + case PivotTableField::VALUES: + aProp.setProperty( CREATE_OUSTRING("Orientation"), DataPilotFieldOrientation_DATA ); + break; + default: + OSL_ENSURE( false, "OoxPivotTableFragment::commit: unhandled case" ); + } + } + CellAddress aCell; + aCell.Sheet = aData.maRange.Sheet; + aCell.Column = aData.maRange.StartColumn; + aCell.Row = aData.maRange.StartRow; + if ( bPageAxisExists ) + aCell.Row -= 2; + + xDPTables->insertNewByName( aName, aCell, xDPDesc ); + } + catch ( const Exception& ) + { + OSL_ENSURE( false, "OoxPivotTableFragment::commit: exception thrown"); + return; + } +} + +bool PivotTableBuffer::getSourceRange( const OUString& aSheetName, const OUString& aRefName, + CellRangeAddress& rRange ) const +{ + sal_Int32 nCount = getWorksheets().getInternalSheetCount(); + for ( sal_Int32 nSheet = 0; nSheet < nCount; ++nSheet ) + { + Reference< XNamed > xNamed( getSheet( nSheet ), UNO_QUERY ); + if ( xNamed.is() && !aSheetName.compareTo( xNamed->getName() ) ) + return getAddressConverter().convertToCellRange( + rRange, aRefName, static_cast< sal_Int16 >( nSheet ), true ); + } + return false; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox diff --git a/oox/source/xls/pivottablefragment.cxx b/oox/source/xls/pivottablefragment.cxx new file mode 100644 index 000000000000..48f99cb4d41c --- /dev/null +++ b/oox/source/xls/pivottablefragment.cxx @@ -0,0 +1,194 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pivottablefragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/pivottablefragment.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/xls/addressconverter.hxx" + +#define DEBUG_OOX_PIVOTTABLE 1 + +#include <vector> +#include <stdexcept> +#if DEBUG_OOX_PIVOTTABLE +#include <stdio.h> +#endif + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::xml::sax::SAXException; + +namespace oox { +namespace xls { + +OoxPivotTableFragment::OoxPivotTableFragment( + const WorksheetHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorksheetFragmentBase( rHelper, rFragmentPath ), + mbValidRange( false ) +{ +} + +bool OoxPivotTableFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: return (nElement == XLS_TOKEN( pivotTableDefinition )); + case XLS_TOKEN( pivotTableDefinition ): return (nElement == XLS_TOKEN( location )) || + (nElement == XLS_TOKEN( pivotFields )) || + (nElement == XLS_TOKEN( rowFields )) || + (nElement == XLS_TOKEN( rowItems )) || + (nElement == XLS_TOKEN( colFields )) || + (nElement == XLS_TOKEN( colItems )) || + (nElement == XLS_TOKEN( pageFields )) || + (nElement == XLS_TOKEN( dataFields )) || + (nElement == XLS_TOKEN( pivotTableStyleInfo )); + case XLS_TOKEN( pivotFields ): return (nElement == XLS_TOKEN( pivotField )); + case XLS_TOKEN( pivotField ): return (nElement == XLS_TOKEN( items )); + case XLS_TOKEN( items ): return (nElement == XLS_TOKEN( item )); + case XLS_TOKEN( rowFields ): return (nElement == XLS_TOKEN( field )); + case XLS_TOKEN( colFields ): return (nElement == XLS_TOKEN( field )); + case XLS_TOKEN( pageFields ): return (nElement == XLS_TOKEN( pageField )); + case XLS_TOKEN( dataFields ): return (nElement == XLS_TOKEN( dataField )); + case XLS_TOKEN( colItems ): return (nElement == XLS_TOKEN( i )); + case XLS_TOKEN( rowItems ): return (nElement == XLS_TOKEN( i )); + } + return false; +} + +void OoxPivotTableFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch ( getCurrentContext() ) + { + case XLS_TOKEN( pivotTableDefinition ): + importPivotTableDefinition( rAttribs ); + break; + case XLS_TOKEN( location ): + importLocation( rAttribs ); + break; + case XLS_TOKEN( pivotFields ): + importPivotFields( rAttribs ); + break; + case XLS_TOKEN( pivotField ): + importPivotField( rAttribs ); + break; + } +} + +void OoxPivotTableFragment::finalizeImport() +{ + if( mbValidRange ) + getPivotTables().setPivotTable( maName, maData ); +} + +void OoxPivotTableFragment::importLocation( const AttributeList& rAttribs ) +{ + CellRangeAddress aRange; + OUString aRangeName = rAttribs.getString( XML_ref ); + mbValidRange = getAddressConverter().convertToCellRange( + aRange, aRangeName, getSheetIndex(), true ); + + if ( mbValidRange ) + maData.maRange = aRange; +} + +void OoxPivotTableFragment::importPivotTableDefinition( const AttributeList& rAttribs ) +{ + if ( !rAttribs.hasAttribute( XML_cacheId ) ) + return; + + maName = rAttribs.getString( XML_name ); + maData.mnCacheId = rAttribs.getInteger( XML_cacheId, 0 ); + + // name="PivotTable3" + // cacheId="0" + // applyNumberFormats="0" + // applyBorderFormats="0" + // applyFontFormats="0" + // applyPatternFormats="0" + // applyAlignmentFormats="0" + // applyWidthHeightFormats="1" + // dataCaption="Values" + // updatedVersion="3" + // minRefreshableVersion="3" + // showCalcMbrs="0" + // useAutoFormatting="1" + // itemPrintTitles="1" + // createdVersion="3" + // indent="0" + // outline="1" + // outlineData="1" + // multipleFieldFilters="0" +} + +void OoxPivotTableFragment::importPivotFields( const AttributeList& rAttribs ) +{ + maData.maFields.reserve( rAttribs.getUnsignedInteger( XML_count, 1 ) ); +} + +void OoxPivotTableFragment::importPivotField( const AttributeList& rAttribs ) +{ + maData.maFields.push_back( PivotTableField() ); + PivotTableField& rField = maData.maFields.back(); + rField.mbDataField = rAttribs.getBool( XML_dataField, false ); + + // Possible values are: axisCol, axisRow, axisPage, axisValues + switch ( rAttribs.getToken( XML_axis ) ) + { + case XML_axisCol: + rField.meAxis = PivotTableField::COLUMN; + break; + case XML_axisRow: + rField.meAxis = PivotTableField::ROW; + break; + case XML_axisPage: + rField.meAxis = PivotTableField::PAGE; + break; + case XML_axisValues: + rField.meAxis = PivotTableField::VALUES; + break; + default: + break; + } +} + +} // namespace xls +} // namespace oox diff --git a/oox/source/xls/querytablefragment.cxx b/oox/source/xls/querytablefragment.cxx new file mode 100644 index 000000000000..703ec9f9f156 --- /dev/null +++ b/oox/source/xls/querytablefragment.cxx @@ -0,0 +1,82 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: querytablefragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/querytablefragment.hxx" +#include "oox/xls/webquerybuffer.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::RuntimeException; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::xml::sax::SAXException; + +namespace oox { +namespace xls { + +OoxQueryTableFragment::OoxQueryTableFragment( + const WorkbookHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ) +{ +} + +bool OoxQueryTableFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: return (nElement == XLS_TOKEN( queryTable )); + } + return false; +} + +void OoxQueryTableFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch ( getCurrentContext() ) + { + case XLS_TOKEN( queryTable ): + importQueryTable( rAttribs ); + break; + } +} + +void OoxQueryTableFragment::importQueryTable( const AttributeList& rAttribs ) +{ + getWebQueries().importQueryTable( rAttribs ); +} + +} // namespace xls +} // namespace oox diff --git a/oox/source/xls/richstring.cxx b/oox/source/xls/richstring.cxx new file mode 100644 index 000000000000..97cc1cbe82c2 --- /dev/null +++ b/oox/source/xls/richstring.cxx @@ -0,0 +1,605 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: richstring.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/richstring.hxx" +#include <com/sun/star/text/XText.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" + +using ::rtl::OString; +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::text::XText; +using ::com::sun::star::text::XTextRange; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt8 OOBIN_STRINGFLAG_FONTS = 0x01; +const sal_uInt8 OOBIN_STRINGFLAG_PHONETICS = 0x02; + +} // namespace + +// ============================================================================ + +RichStringPortion::RichStringPortion( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnFontId( -1 ) +{ +} + +void RichStringPortion::setText( const OUString& rText ) +{ + maText = rText; +} + +FontRef RichStringPortion::importFont( const AttributeList& ) +{ + mxFont.reset( new Font( *this, false ) ); + return mxFont; +} + +void RichStringPortion::setFontId( sal_Int32 nFontId ) +{ + mnFontId = nFontId; +} + +void RichStringPortion::finalizeImport() +{ + if( mxFont.get() ) + mxFont->finalizeImport(); + else if( mnFontId >= 0 ) + mxFont = getStyles().getFont( mnFontId ); +} + +void RichStringPortion::convert( const Reference< XText >& rxText, sal_Int32 nXfId ) +{ + Reference< XTextRange > xRange = rxText->getEnd(); + xRange->setString( maText ); + if( mxFont.get() ) + { + PropertySet aPropSet( xRange ); + mxFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_RICHTEXT ); + } + if( const Font* pFont = getStyles().getFontFromCellXf( nXfId ).get() ) + { + if( pFont->needsRichTextFormat() ) + { + PropertySet aPropSet( xRange ); + pFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_RICHTEXT ); + } + } +} + +// ---------------------------------------------------------------------------- + +void BinFontPortionData::read( RecordInputStream& rStrm ) +{ + mnPos = rStrm.readuInt16(); + mnFontId = rStrm.readuInt16(); +} + +void BinFontPortionData::read( BiffInputStream& rStrm, bool b16Bit ) +{ + if( b16Bit ) + { + mnPos = rStrm.readuInt16(); + mnFontId = rStrm.readuInt16(); + } + else + { + mnPos = rStrm.readuInt8(); + mnFontId = rStrm.readuInt8(); + } +} + +// ---------------------------------------------------------------------------- + +void BinFontPortionList::appendPortion( const BinFontPortionData& rPortion ) +{ + // #i33341# real life -- same character index may occur several times + OSL_ENSURE( empty() || (back().mnPos <= rPortion.mnPos), "BinFontPortionList::appendPortion - wrong char order" ); + if( empty() || (back().mnPos < rPortion.mnPos) ) + push_back( rPortion ); + else + back().mnFontId = rPortion.mnFontId; +} + +void BinFontPortionList::importPortions( RecordInputStream& rStrm ) +{ + sal_Int32 nCount = rStrm.readInt32(); + clear(); + if( nCount > 0 ) + { + reserve( getLimitedValue< size_t, sal_Int32 >( nCount, 0, rStrm.getRecLeft() / 4 ) ); + /* #i33341# real life -- same character index may occur several times + -> use appendPortion() to validate string position. */ + BinFontPortionData aPortion; + for( sal_Int32 nIndex = 0; rStrm.isValid() && (nIndex < nCount); ++nIndex ) + { + aPortion.read( rStrm ); + appendPortion( aPortion ); + } + } +} + +void BinFontPortionList::importPortions( BiffInputStream& rStrm, sal_uInt16 nCount, bool b16Bit ) +{ + clear(); + reserve( nCount ); + /* #i33341# real life -- same character index may occur several times + -> use appendPortion() to validate string position. */ + BinFontPortionData aPortion; + for( sal_uInt16 nIndex = 0; rStrm.isValid() && (nIndex < nCount); ++nIndex ) + { + aPortion.read( rStrm, b16Bit ); + appendPortion( aPortion ); + } +} + +void BinFontPortionList::importPortions( BiffInputStream& rStrm, bool b16Bit ) +{ + sal_uInt16 nCount = b16Bit ? rStrm.readuInt16() : rStrm.readuInt8(); + importPortions( rStrm, nCount, b16Bit ); +} + +// ============================================================================ + +OoxPhoneticData::OoxPhoneticData() : + mnFontId( -1 ), + mnType( XML_fullwidthKatakana ), + mnAlignment( XML_left ) +{ +} + +void OoxPhoneticData::setBinData( sal_Int32 nType, sal_Int32 nAlignment ) +{ + static const sal_Int32 spnTypeIds[] = { XML_halfwidthKatakana, XML_fullwidthKatakana, XML_hiragana, XML_noConversion }; + mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_fullwidthKatakana ); + + static const sal_Int32 spnAlignments[] = { XML_noControl, XML_left, XML_center, XML_distributed }; + mnAlignment = STATIC_ARRAY_SELECT( spnAlignments, nAlignment, XML_left ); +} + +// ---------------------------------------------------------------------------- + +PhoneticSettings::PhoneticSettings( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void PhoneticSettings::importPhoneticPr( const AttributeList& rAttribs ) +{ + maOoxData.mnFontId = rAttribs.getInteger( XML_fontId, -1 ); + maOoxData.mnType = rAttribs.getToken( XML_type, XML_fullwidthKatakana ); + maOoxData.mnAlignment = rAttribs.getToken( XML_alignment, XML_left ); +} + +void PhoneticSettings::importPhoneticPr( RecordInputStream& rStrm ) +{ + sal_uInt16 nFontId; + sal_Int32 nType, nAlignment; + rStrm >> nFontId >> nType >> nAlignment; + maOoxData.mnFontId = nFontId; + maOoxData.setBinData( nType, nAlignment ); +} + +void PhoneticSettings::importPhoneticPr( BiffInputStream& rStrm ) +{ + sal_uInt16 nFontId, nFlags; + rStrm >> nFontId >> nFlags; + maOoxData.mnFontId = nFontId; + maOoxData.setBinData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) ); + // following: range list with cells showing phonetic text +} + +void PhoneticSettings::importStringData( RecordInputStream& rStrm ) +{ + sal_uInt16 nFontId, nFlags; + rStrm >> nFontId >> nFlags; + maOoxData.mnFontId = nFontId; + maOoxData.setBinData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) ); +} + +void PhoneticSettings::importStringData( BiffInputStream& rStrm ) +{ + sal_uInt16 nFontId, nFlags; + rStrm >> nFontId >> nFlags; + maOoxData.mnFontId = nFontId; + maOoxData.setBinData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) ); +} + +// ============================================================================ + +RichStringPhonetic::RichStringPhonetic( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnBasePos( -1 ), + mnBaseEnd( -1 ) +{ +} + +void RichStringPhonetic::setText( const OUString& rText ) +{ + maText = rText; +} + +void RichStringPhonetic::importPhoneticRun( const AttributeList& rAttribs ) +{ + mnBasePos = rAttribs.getInteger( XML_sb, -1 ); + mnBaseEnd = rAttribs.getInteger( XML_eb, -1 ); +} + +void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd ) +{ + mnBasePos = nBasePos; + mnBaseEnd = nBaseEnd; +} + +// ---------------------------------------------------------------------------- + +void BinPhoneticPortionData::read( RecordInputStream& rStrm ) +{ + mnPos = rStrm.readuInt16(); + mnBasePos = rStrm.readuInt16(); + mnBaseLen = rStrm.readuInt16(); +} + +void BinPhoneticPortionData::read( BiffInputStream& rStrm ) +{ + mnPos = rStrm.readuInt16(); + mnBasePos = rStrm.readuInt16(); + mnBaseLen = rStrm.readuInt16(); +} + +// ---------------------------------------------------------------------------- + +void BinPhoneticPortionList::appendPortion( const BinPhoneticPortionData& rPortion ) +{ + // same character index may occur several times + OSL_ENSURE( empty() || ((back().mnPos <= rPortion.mnPos) && + (back().mnBasePos + back().mnBaseLen <= rPortion.mnBasePos)), + "BinPhoneticPortionList::appendPortion - wrong char order" ); + if( empty() || (back().mnPos < rPortion.mnPos) ) + { + push_back( rPortion ); + } + else if( back().mnPos == rPortion.mnPos ) + { + back().mnBasePos = rPortion.mnBasePos; + back().mnBaseLen = rPortion.mnBaseLen; + } +} + +void BinPhoneticPortionList::importPortions( RecordInputStream& rStrm ) +{ + sal_Int32 nCount = rStrm.readInt32(); + clear(); + if( nCount > 0 ) + { + reserve( getLimitedValue< size_t, sal_Int32 >( nCount, 0, rStrm.getRecLeft() / 6 ) ); + BinPhoneticPortionData aPortion; + for( sal_Int32 nIndex = 0; rStrm.isValid() && (nIndex < nCount); ++nIndex ) + { + aPortion.read( rStrm ); + appendPortion( aPortion ); + } + } +} + +OUString BinPhoneticPortionList::importPortions( BiffInputStream& rStrm, sal_uInt32 nPhoneticSize ) +{ + OUString aPhoneticText; + sal_uInt16 nPortionCount, nTextLen1, nTextLen2; + rStrm >> nPortionCount >> nTextLen1 >> nTextLen2; + OSL_ENSURE( nTextLen1 == nTextLen2, "BinPhoneticPortionList::importPortions - wrong phonetic text length" ); + if( (nTextLen1 == nTextLen2) && (nTextLen1 > 0) ) + { + sal_uInt32 nMinSize = static_cast< sal_uInt32 >( 2 * nTextLen1 + 6 * nPortionCount + 14 ); + OSL_ENSURE( nMinSize <= nPhoneticSize, "BinPhoneticPortionList::importPortions - wrong size of phonetic data" ); + if( nMinSize <= nPhoneticSize ) + { + aPhoneticText = rStrm.readUnicodeArray( nTextLen1 ); + clear(); + reserve( nPortionCount ); + BinPhoneticPortionData aPortion; + for( sal_uInt16 nPortion = 0; nPortion < nPortionCount; ++nPortion ) + { + aPortion.read( rStrm ); + appendPortion( aPortion ); + } + } + } + return aPhoneticText; +} + +// ============================================================================ + +RichString::RichString( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maPhonSettings( rHelper ) +{ +} + +RichStringPortionRef RichString::importText( const AttributeList& ) +{ + return createPortion(); +} + +RichStringPortionRef RichString::importRun( const AttributeList& ) +{ + return createPortion(); +} + +RichStringPhoneticRef RichString::importPhoneticRun( const AttributeList& rAttribs ) +{ + RichStringPhoneticRef xPhonetic = createPhonetic(); + xPhonetic->importPhoneticRun( rAttribs ); + return xPhonetic; +} + +void RichString::importPhoneticPr( const AttributeList& rAttribs ) +{ + maPhonSettings.importPhoneticPr( rAttribs ); +} + +void RichString::importString( RecordInputStream& rStrm, bool bRich ) +{ + sal_uInt8 nFlags = bRich ? rStrm.readuInt8() : 0; + OUString aBaseText = rStrm.readString(); + + if( rStrm.isValid() && getFlag( nFlags, OOBIN_STRINGFLAG_FONTS ) ) + { + BinFontPortionList aPortions; + aPortions.importPortions( rStrm ); + createFontPortions( aBaseText, aPortions ); + } + else + { + createPortion()->setText( aBaseText ); + } + + if( rStrm.isValid() && getFlag( nFlags, OOBIN_STRINGFLAG_PHONETICS ) ) + { + OUString aPhoneticText = rStrm.readString(); + BinPhoneticPortionList aPortions; + aPortions.importPortions( rStrm ); + maPhonSettings.importStringData( rStrm ); + createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() ); + } +} + +void RichString::importByteString( BiffInputStream& rStrm, rtl_TextEncoding eDefaultTextEnc, BiffStringFlags nFlags ) +{ + OSL_ENSURE( !getFlag( nFlags, BIFF_STR_KEEPFONTS ), "RichString::importString - keep fonts not implemented" ); + OSL_ENSURE( !getFlag( nFlags, static_cast< BiffStringFlags >( ~(BIFF_STR_8BITLENGTH | BIFF_STR_EXTRAFONTS) ) ), "RichString::importByteString - unknown flag" ); + bool b8BitLength = getFlag( nFlags, BIFF_STR_8BITLENGTH ); + + OString aBaseText = rStrm.readByteString( !b8BitLength ); + + if( rStrm.isValid() && getFlag( nFlags, BIFF_STR_EXTRAFONTS ) ) + { + BinFontPortionList aPortions; + aPortions.importPortions( rStrm, false ); + createFontPortions( aBaseText, eDefaultTextEnc, aPortions ); + } + else + { + createPortion()->setText( OStringToOUString( aBaseText, eDefaultTextEnc ) ); + } +} + +void RichString::importUniString( BiffInputStream& rStrm, BiffStringFlags nFlags ) +{ + OSL_ENSURE( !getFlag( nFlags, BIFF_STR_KEEPFONTS ), "RichString::importUniString - keep fonts not implemented" ); + OSL_ENSURE( !getFlag( nFlags, static_cast< BiffStringFlags >( ~(BIFF_STR_8BITLENGTH | BIFF_STR_SMARTFLAGS) ) ), "RichString::importUniString - unknown flag" ); + bool b8BitLength = getFlag( nFlags, BIFF_STR_8BITLENGTH ); + + // --- string header --- + sal_uInt16 nChars = b8BitLength ? rStrm.readuInt8() : rStrm.readuInt16(); + sal_uInt8 nFlagField = 0; + if( (nChars > 0) || !getFlag( nFlags, BIFF_STR_SMARTFLAGS ) ) + rStrm >> nFlagField; + + bool b16Bit, bFonts, bPhonetic; + sal_uInt16 nFontCount; + sal_uInt32 nPhoneticSize; + rStrm.readExtendedUniStringHeader( b16Bit, bFonts, bPhonetic, nFontCount, nPhoneticSize, nFlagField ); + + // --- character array --- + OUString aBaseText = rStrm.readRawUniString( nChars, b16Bit ); + + // --- formatting --- + // #122185# bRich flag may be set, but format runs may be missing + if( rStrm.isValid() && (nFontCount > 0) ) + { + BinFontPortionList aPortions; + aPortions.importPortions( rStrm, nFontCount, true ); + createFontPortions( aBaseText, aPortions ); + } + else + { + createPortion()->setText( aBaseText ); + } + + // --- Asian phonetic information --- + // #122185# bPhonetic flag may be set, but phonetic info may be missing + if( rStrm.isValid() && (nPhoneticSize > 0) ) + { + sal_uInt32 nPhoneticEnd = rStrm.getRecPos() + nPhoneticSize; + OSL_ENSURE( nPhoneticSize > 14, "RichString::importUniString - wrong size of phonetic data" ); + if( nPhoneticSize > 14 ) + { + sal_uInt16 nId, nSize; + rStrm >> nId >> nSize; + OSL_ENSURE( nId == 1, "RichString::importUniString - unknown phonetic data identifier" ); + sal_uInt32 nMinSize = static_cast< sal_uInt32 >( nSize + 4 ); + OSL_ENSURE( nMinSize <= nPhoneticSize, "RichString::importUniString - wrong size of phonetic data" ); + if( (nId == 1) && (nMinSize <= nPhoneticSize) ) + { + maPhonSettings.importStringData( rStrm ); + BinPhoneticPortionList aPortions; + OUString aPhoneticText = aPortions.importPortions( rStrm, nPhoneticSize ); + createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() ); + } + } + rStrm.seek( nPhoneticEnd ); + } +} + +void RichString::finalizeImport() +{ + maFontPortions.forEachMem( &RichStringPortion::finalizeImport ); +} + +void RichString::convert( const Reference< XText >& rxText, sal_Int32 nXfId ) const +{ + for( PortionVec::const_iterator aIt = maFontPortions.begin(), aEnd = maFontPortions.end(); aIt != aEnd; ++aIt ) + { + (*aIt)->convert( rxText, nXfId ); + nXfId = -1; + } +} + +// private -------------------------------------------------------------------- + +RichStringPortionRef RichString::createPortion() +{ + RichStringPortionRef xPortion( new RichStringPortion( *this ) ); + maFontPortions.push_back( xPortion ); + return xPortion; +} + +RichStringPhoneticRef RichString::createPhonetic() +{ + RichStringPhoneticRef xPhonetic( new RichStringPhonetic( *this ) ); + maPhonPortions.push_back( xPhonetic ); + return xPhonetic; +} + +void RichString::createFontPortions( const OString& rText, rtl_TextEncoding eDefaultTextEnc, BinFontPortionList& rPortions ) +{ + maFontPortions.clear(); + sal_Int32 nStrLen = rText.getLength(); + if( nStrLen > 0 ) + { + // add leading and trailing string position to ease the following loop + if( rPortions.empty() || (rPortions.front().mnPos > 0) ) + rPortions.insert( rPortions.begin(), BinFontPortionData( 0, -1 ) ); + if( rPortions.back().mnPos < nStrLen ) + rPortions.push_back( BinFontPortionData( nStrLen, -1 ) ); + + // create all string portions according to the font id vector + for( BinFontPortionList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt ) + { + sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos; + if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) ) + { + // convert byte string to unicode string, using current font encoding + FontRef xFont = getStyles().getFont( aIt->mnFontId ); + rtl_TextEncoding eTextEnc = xFont.get() ? xFont->getFontEncoding() : eDefaultTextEnc; + OUString aUniStr = OStringToOUString( rText.copy( aIt->mnPos, nPortionLen ), eTextEnc ); + // create string portion + RichStringPortionRef xPortion = createPortion(); + xPortion->setText( aUniStr ); + xPortion->setFontId( aIt->mnFontId ); + } + } + } +} + +void RichString::createFontPortions( const OUString& rText, BinFontPortionList& rPortions ) +{ + maFontPortions.clear(); + sal_Int32 nStrLen = rText.getLength(); + if( nStrLen > 0 ) + { + // add leading and trailing string position to ease the following loop + if( rPortions.empty() || (rPortions.front().mnPos > 0) ) + rPortions.insert( rPortions.begin(), BinFontPortionData( 0, -1 ) ); + if( rPortions.back().mnPos < nStrLen ) + rPortions.push_back( BinFontPortionData( nStrLen, -1 ) ); + + // create all string portions according to the font id vector + for( BinFontPortionList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt ) + { + sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos; + if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) ) + { + RichStringPortionRef xPortion = createPortion(); + xPortion->setText( rText.copy( aIt->mnPos, nPortionLen ) ); + xPortion->setFontId( aIt->mnFontId ); + } + } + } +} + +void RichString::createPhoneticPortions( const ::rtl::OUString& rText, BinPhoneticPortionList& rPortions, sal_Int32 nBaseLen ) +{ + maPhonPortions.clear(); + sal_Int32 nStrLen = rText.getLength(); + if( nStrLen > 0 ) + { + // no portions - assign phonetic text to entire base text + if( rPortions.empty() ) + rPortions.push_back( BinPhoneticPortionData( 0, 0, nBaseLen ) ); + // add trailing string position to ease the following loop + if( rPortions.back().mnPos < nStrLen ) + rPortions.push_back( BinPhoneticPortionData( nStrLen, nBaseLen, 0 ) ); + + // create all phonetic portions according to the portions vector + for( BinPhoneticPortionList::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt ) + { + sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos; + if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) ) + { + RichStringPhoneticRef xPhonetic = createPhonetic(); + xPhonetic->setText( rText.copy( aIt->mnPos, nPortionLen ) ); + xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen ); + } + } + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/richstringcontext.cxx b/oox/source/xls/richstringcontext.cxx new file mode 100644 index 000000000000..fd9b21572cb6 --- /dev/null +++ b/oox/source/xls/richstringcontext.cxx @@ -0,0 +1,118 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: richstringcontext.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/richstringcontext.hxx" + +using ::rtl::OUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxRichStringContext::onCanCreateContext( sal_Int32 nElement ) const +{ + sal_Int32 nCurrContext = getCurrentContext(); + switch( nCurrContext ) + { + case XLS_TOKEN( si ): + case XLS_TOKEN( is ): + case XLS_TOKEN( text ): + return (nElement == XLS_TOKEN( t )) || + (nElement == XLS_TOKEN( r )) || + (nElement == XLS_TOKEN( rPh )) || + (nElement == XLS_TOKEN( phoneticPr )); + case XLS_TOKEN( r ): + return (nElement == XLS_TOKEN( rPr )) || + (nElement == XLS_TOKEN( t )); + case XLS_TOKEN( rPh ): + return (nElement == XLS_TOKEN( t )); + case XLS_TOKEN( rPr ): + return Font::isSupportedContext( nElement, nCurrContext ); + } + return false; +} + +void OoxRichStringContext::onStartElement( const AttributeList& rAttribs ) +{ + sal_Int32 nCurrContext = getCurrentContext(); + switch( nCurrContext ) + { + case XLS_TOKEN( t ): + if( !isPreviousContext( XLS_TOKEN( r ) ) && !isPreviousContext( XLS_TOKEN( rPh ) ) ) + mxPortion = mxString->importText( rAttribs ); + break; + case XLS_TOKEN( r ): + mxPortion = mxString->importRun( rAttribs ); + break; + case XLS_TOKEN( rPr ): + if( mxPortion.get() ) mxFont = mxPortion->importFont( rAttribs ); + break; + case XLS_TOKEN( rPh ): + mxPhonetic = mxString->importPhoneticRun( rAttribs ); + break; + case XLS_TOKEN( phoneticPr ): + mxString->importPhoneticPr( rAttribs ); + break; + default: + if( isPreviousContext( XLS_TOKEN( rPr ) ) && mxFont.get() ) + mxFont->importAttribs( nCurrContext, rAttribs ); + } +} + +void OoxRichStringContext::onEndElement( const OUString& rChars ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( t ): + switch( getPreviousContext() ) + { + case XLS_TOKEN( rPh ): + if( mxPhonetic.get() ) mxPhonetic->setText( rChars ); + break; + default: + if( mxPortion.get() ) mxPortion->setText( rChars ); + } + break; + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/sharedformulabuffer.cxx b/oox/source/xls/sharedformulabuffer.cxx new file mode 100644 index 000000000000..5a09ec22c407 --- /dev/null +++ b/oox/source/xls/sharedformulabuffer.cxx @@ -0,0 +1,220 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sharedformulabuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/sharedformulabuffer.hxx" +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/sheet/XFormulaTokens.hpp> +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/defnamesbuffer.hxx" +#include "oox/xls/formulaparser.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::sheet::XFormulaTokens; +using ::com::sun::star::sheet::XNamedRange; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +bool operator==( const CellAddress& rAddr1, const CellAddress& rAddr2 ) +{ + return + (rAddr1.Sheet == rAddr2.Sheet) && + (rAddr1.Column == rAddr2.Column) && + (rAddr1.Row == rAddr2.Row); +} + +bool lclContains( const CellRangeAddress& rRange, const CellAddress& rAddr ) +{ + return + (rRange.Sheet == rAddr.Sheet) && + (rRange.StartColumn <= rAddr.Column) && (rAddr.Column <= rRange.EndColumn) && + (rRange.StartRow <= rAddr.Row) && (rAddr.Row <= rRange.EndRow); +} + +} // namespace + +// ============================================================================ + +ExtCellFormulaContext::ExtCellFormulaContext( const WorksheetHelper& rHelper, + const Reference< XFormulaTokens >& rxTokens, const CellAddress& rCellPos ) : + SimpleFormulaContext( rxTokens, false, false ), + WorksheetHelper( rHelper ) +{ + setBaseAddress( rCellPos ); +} + +void ExtCellFormulaContext::setSharedFormula( const CellAddress& rBaseAddr ) +{ + getSharedFormulas().setSharedFormulaCell( *this, rBaseAddr ); +} + +// ============================================================================ + +SharedFormulaBuffer::SharedFormulaBuffer( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ) +{ +} + +void SharedFormulaBuffer::importSharedFmla( const OUString& rFormula, const OUString& rSharedRange, sal_Int32 nSharedId, const CellAddress& rBaseAddr ) +{ + CellRangeAddress aFmlaRange; + if( getAddressConverter().convertToCellRange( aFmlaRange, rSharedRange, getSheetIndex(), true ) ) + { + // create the defined name representing the shared formula + OSL_ENSURE( lclContains( aFmlaRange, rBaseAddr ), "SharedFormulaBuffer::importSharedFmla - invalid range for shared formula" ); + BinAddress aMapKey( nSharedId, 0 ); + Reference< XNamedRange > xNamedRange = createDefinedName( aMapKey ); + // convert the formula definition + Reference< XFormulaTokens > xTokens( xNamedRange, UNO_QUERY ); + if( xTokens.is() ) + { + SimpleFormulaContext aContext( xTokens, true, false ); + aContext.setBaseAddress( rBaseAddr ); + getFormulaParser().importFormula( aContext, rFormula ); + updateCachedCell( rBaseAddr, aMapKey ); + } + } +} + +void SharedFormulaBuffer::importSharedFmla( RecordInputStream& rStrm, const CellAddress& rBaseAddr ) +{ + BinRange aRange; + rStrm >> aRange; + CellRangeAddress aFmlaRange; + if( getAddressConverter().convertToCellRange( aFmlaRange, aRange, getSheetIndex(), true ) ) + { + // create the defined name representing the shared formula + OSL_ENSURE( lclContains( aFmlaRange, rBaseAddr ), "SharedFormulaBuffer::importSharedFmla - invalid range for shared formula" ); + BinAddress aMapKey( rBaseAddr ); + Reference< XNamedRange > xNamedRange = createDefinedName( aMapKey ); + // load the formula definition + Reference< XFormulaTokens > xTokens( xNamedRange, UNO_QUERY ); + if( xTokens.is() ) + { + SimpleFormulaContext aContext( xTokens, true, false ); + aContext.setBaseAddress( rBaseAddr ); + getFormulaParser().importFormula( aContext, rStrm ); + updateCachedCell( rBaseAddr, aMapKey ); + } + } +} + +void SharedFormulaBuffer::importSharedFmla( BiffInputStream& rStrm, const CellAddress& rBaseAddr ) +{ + BinRange aRange; + aRange.read( rStrm, false ); // always 8bit column indexes + CellRangeAddress aFmlaRange; + if( getAddressConverter().convertToCellRange( aFmlaRange, aRange, getSheetIndex(), true ) ) + { + // create the defined name representing the shared formula + OSL_ENSURE( lclContains( aFmlaRange, rBaseAddr ), "SharedFormulaBuffer::importSharedFmla - invalid range for shared formula" ); + BinAddress aMapKey( rBaseAddr ); + Reference< XNamedRange > xNamedRange = createDefinedName( aMapKey ); + // load the formula definition + Reference< XFormulaTokens > xTokens( xNamedRange, UNO_QUERY ); + if( xTokens.is() ) + { + rStrm.skip( 2 ); // flags + SimpleFormulaContext aContext( xTokens, true, false ); + aContext.setBaseAddress( rBaseAddr ); + getFormulaParser().importFormula( aContext, rStrm ); + updateCachedCell( rBaseAddr, aMapKey ); + } + } +} + +void SharedFormulaBuffer::setSharedFormulaCell( ExtCellFormulaContext& rContext, const CellAddress& rBaseAddr ) +{ + if( !implSetSharedFormulaCell( rContext, BinAddress( rBaseAddr ) ) ) + if( rContext.getBaseAddress() == rBaseAddr ) + mxLastContext.reset( new ExtCellFormulaContext( rContext ) ); +} + +void SharedFormulaBuffer::setSharedFormulaCell( ExtCellFormulaContext& rContext, sal_Int32 nSharedId ) +{ + implSetSharedFormulaCell( rContext, BinAddress( nSharedId, 0 ) ); +} + +Reference< XNamedRange > SharedFormulaBuffer::createDefinedName( const BinAddress& rMapKey ) +{ + OSL_ENSURE( maIndexMap.count( rMapKey ) == 0, "SharedFormulaBuffer::createDefinedName - shared formula exists already" ); + // create the defined name representing the shared formula + OUString aName = OUStringBuffer().appendAscii( "__shared_" ). + append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) ). + append( sal_Unicode( '_' ) ).append( rMapKey.mnRow ). + append( sal_Unicode( '_' ) ).append( rMapKey.mnCol ).makeStringAndClear(); + Reference< XNamedRange > xNamedRange = getDefinedNames().createDefinedName( aName ); + sal_Int32 nTokenIndex = getDefinedNames().getTokenIndex( xNamedRange ); + if( nTokenIndex >= 0 ) + maIndexMap[ rMapKey ] = nTokenIndex; + return xNamedRange; +} + +bool SharedFormulaBuffer::implSetSharedFormulaCell( ExtCellFormulaContext& rContext, const BinAddress& rMapKey ) +{ + TokenIndexMap::const_iterator aIt = maIndexMap.find( rMapKey ); + sal_Int32 nTokenIndex = (aIt == maIndexMap.end()) ? -1 : aIt->second; + if( nTokenIndex >= 0 ) + { + getFormulaParser().convertNameToFormula( rContext, nTokenIndex ); + return true; + } + return false; +} + +void SharedFormulaBuffer::updateCachedCell( const CellAddress& rBaseAddr, const BinAddress& rMapKey ) +{ + if( mxLastContext.get() && (mxLastContext->getBaseAddress() == rBaseAddr) ) + implSetSharedFormulaCell( *mxLastContext, rMapKey ); + mxLastContext.reset(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/sharedstringsbuffer.cxx b/oox/source/xls/sharedstringsbuffer.cxx new file mode 100644 index 000000000000..85a6662e3004 --- /dev/null +++ b/oox/source/xls/sharedstringsbuffer.cxx @@ -0,0 +1,91 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sharedstringsbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/sharedstringsbuffer.hxx" +#include "oox/xls/biffinputstream.hxx" + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::text::XText; + +namespace oox { +namespace xls { + +// ============================================================================ + +SharedStringsBuffer::SharedStringsBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +RichStringRef SharedStringsBuffer::createRichString() +{ + RichStringRef xString( new RichString( *this ) ); + maStrings.push_back( xString ); + return xString; +} + +void SharedStringsBuffer::importSst( BiffInputStream& rStrm ) +{ + sal_Int32 nStringCount = rStrm.skip( 4 ).readInt32(); + if( nStringCount > 0 ) + { + maStrings.clear(); + maStrings.reserve( static_cast< size_t >( nStringCount ) ); + for( ; rStrm.isValid() && (nStringCount > 0); --nStringCount ) + { + RichStringRef xString( new RichString( *this ) ); + maStrings.push_back( xString ); + xString->importUniString( rStrm ); + } + } +} + +void SharedStringsBuffer::finalizeImport() +{ + maStrings.forEachMem( &RichString::finalizeImport ); +} + +void SharedStringsBuffer::convertString( const Reference< XText >& rxText, sal_Int32 nStringId, sal_Int32 nXfId ) const +{ + if( rxText.is() ) + if( const RichString* pString = maStrings.get( nStringId ).get() ) + pString->convert( rxText, nXfId ); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/sharedstringsfragment.cxx b/oox/source/xls/sharedstringsfragment.cxx new file mode 100644 index 000000000000..8b7b1c5d6b6e --- /dev/null +++ b/oox/source/xls/sharedstringsfragment.cxx @@ -0,0 +1,110 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sharedstringsfragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/sharedstringsfragment.hxx" +#include "oox/xls/sharedstringsbuffer.hxx" +#include "oox/xls/richstringcontext.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::xml::sax::XFastContextHandler; + +namespace oox { +namespace xls { + +// ============================================================================ + +OoxSharedStringsFragment::OoxSharedStringsFragment( + const WorkbookHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxSharedStringsFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nElement == XLS_TOKEN( sst )); + case XLS_TOKEN( sst ): + return (nElement == XLS_TOKEN( si )); + } + return false; +} + +Reference< XFastContextHandler > OoxSharedStringsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) +{ + switch( nElement ) + { + case XLS_TOKEN( si ): + return new OoxRichStringContext( *this, getSharedStrings().createRichString() ); + } + return this; +} + +bool OoxSharedStringsFragment::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nRecId == OOBIN_ID_SST); + case OOBIN_ID_SST: + return (nRecId == OOBIN_ID_SI); + } + return false; +} + +void OoxSharedStringsFragment::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_SI: getSharedStrings().createRichString()->importString( rStrm, true ); break; + } +} + +// oox.xls.OoxFragmentHandler interface --------------------------------------- + +void OoxSharedStringsFragment::finalizeImport() +{ + getSharedStrings().finalizeImport(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/sheetcellrangemap.cxx b/oox/source/xls/sheetcellrangemap.cxx new file mode 100644 index 000000000000..aeb35953a558 --- /dev/null +++ b/oox/source/xls/sheetcellrangemap.cxx @@ -0,0 +1,172 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sheetcellrangemap.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2007 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/sheetcellrangemap.hxx" + +#define DEBUG_OOX_CELLRANGE_MAP 0 + +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/table/CellAddress.hpp> + +#if DEBUG_OOX_CELLRANGE_MAP +#include <stdio.h> +#endif + +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; + +namespace oox { +namespace xls { + +SheetCellRangeMap::SheetCellRangeMap() +{ +} + +SheetCellRangeMap::~SheetCellRangeMap() throw() +{ +} + +void SheetCellRangeMap::addCellRange( const CellRangeAddress& aRangeAddr ) +{ + size_t nAreaId = maAreas.size(); + + // First, find the sheet ID. + SheetMapType::iterator posSheet = maSheetMap.find(aRangeAddr.Sheet); + if ( posSheet == maSheetMap.end() ) + { + maSheetMap.insert( SheetMapType::value_type(aRangeAddr.Sheet, SheetSet()) ); + posSheet = maSheetMap.find(aRangeAddr.Sheet); + OSL_ENSURE( posSheet != maSheetMap.end(), "SheetCellRangeMap::addCellRange: insertion failure" ); + } + SheetSet& rSheet = posSheet->second; + + addRange(rSheet.maColRanges, aRangeAddr.StartColumn, aRangeAddr.EndColumn, nAreaId); + addRange(rSheet.maRowRanges, aRangeAddr.StartRow, aRangeAddr.EndRow, nAreaId); + +#if DEBUG_OOX_CELLRANGE_MAP + fprintf(stdout, "SheetCellRangeMap::addCellRange: adding (sheet: %d) (col: %ld - %ld) (row: %ld - %ld) (area: %d)\n", + aRangeAddr.Sheet, aRangeAddr.StartColumn, aRangeAddr.EndColumn, aRangeAddr.StartRow, aRangeAddr.EndRow, nAreaId);fflush(stdout); +#endif + + maAreas.push_back(aRangeAddr); +} + +bool SheetCellRangeMap::isOverlapping( const CellAddress& aCellAddr ) const +{ + if ( maAreas.empty() ) + return false; + + SheetMapType::const_iterator pos = maSheetMap.find(aCellAddr.Sheet); + if ( pos == maSheetMap.end() ) + // There is no cell range registered for this sheet. + return false; + + const SheetSet& rSheet = pos->second; + return searchColumns( rSheet, aCellAddr ); +} + +void SheetCellRangeMap::addRange( StartEndMapType& rRangeMap, sal_Int32 nStart, sal_Int32 nEnd, size_t nAreaId ) +{ + StartEndMapType::iterator posStart = rRangeMap.find(nStart); + if ( posStart == rRangeMap.end() ) + { + EndAreaIdMapType aMap; + rRangeMap.insert( StartEndMapType::value_type(nStart, aMap) ); + posStart = rRangeMap.find(nStart); + OSL_ENSURE( posStart != rRangeMap.end(), "TableBuffer::addRangeToSet: insertion failure" ); + } + EndAreaIdMapType& rEndMap = posStart->second; + + EndAreaIdMapType::iterator posEnd = rEndMap.find(nEnd); + if ( posEnd == rEndMap.end() ) + { + AreaIdSetType aSet; + rEndMap.insert( EndAreaIdMapType::value_type(nEnd, aSet) ); + posEnd = rEndMap.find(nEnd); + OSL_ENSURE( posEnd != rEndMap.end(), "TableBuffer::addRangeToSet: insertion failure" ); + } + + AreaIdSetType& rSet = posEnd->second; + rSet.push_back(nAreaId); +} + +bool SheetCellRangeMap::expandSearch( const EndAreaIdMapType& rEndMap, const CellAddress& rCellAddr, bool bColumn ) const +{ + sal_Int32 nId = bColumn ? rCellAddr.Column : rCellAddr.Row; + + EndAreaIdMapType::const_reverse_iterator itr, itrBeg = rEndMap.rbegin(), itrEnd = rEndMap.rend(); + for ( itr = itrBeg; itr != itrEnd; ++itr ) + { + if ( itr->first >= nId ) + { + // The point is in-range. + const AreaIdSetType& rSet = itr->second; + AreaIdSetType::const_iterator itr2 = rSet.begin(), itr2End = rSet.end(); + for ( ; itr2 != itr2End; ++itr2 ) + { + OSL_ENSURE( maAreas.size() > *itr2, "SheetCellRangeMap::expandSearch: size mismatch" ); + const CellRangeAddress& rRange = maAreas[*itr2]; + if ( bColumn && rCellAddr.Row >= rRange.StartRow && rCellAddr.Row <= rRange.EndRow ) + return true; + if ( !bColumn && rCellAddr.Column >= rRange.StartColumn && rCellAddr.Column <= rRange.EndColumn ) + return true; + } + } + else if ( itr->first < nId ) + // No more enclosing ranges. + return false; + } + return false; +} + +bool SheetCellRangeMap::searchColumns( const SheetSet& rSheet, const CellAddress& aCellAddr ) const +{ + StartEndMapType::const_iterator itr, itrBeg = rSheet.maColRanges.begin(), itrEnd = rSheet.maColRanges.end(); + for ( itr = itrBeg; itr != itrEnd; ++itr ) + { + if ( itr->first <= aCellAddr.Column ) + { + if ( expandSearch(itr->second, aCellAddr, true) ) + return true; + } + else if ( itr->first > aCellAddr.Column ) + return false; + } + return false; +} + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/sheetdatacontext.cxx b/oox/source/xls/sheetdatacontext.cxx new file mode 100644 index 000000000000..b605326f2f07 --- /dev/null +++ b/oox/source/xls/sheetdatacontext.cxx @@ -0,0 +1,1160 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sheetdatacontext.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/sheetdatacontext.hxx" +#include <com/sun/star/table/CellContentType.hpp> +#include <com/sun/star/table/XCell.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/sheet/XFormulaTokens.hpp> +#include <com/sun/star/sheet/XArrayFormulaTokens.hpp> +#include <com/sun/star/text/XText.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/formulaparser.hxx" +#include "oox/xls/pivottablebuffer.hxx" +#include "oox/xls/richstringcontext.hxx" +#include "oox/xls/sharedformulabuffer.hxx" +#include "oox/xls/unitconverter.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::table::CellContentType_EMPTY; +using ::com::sun::star::table::XCell; +using ::com::sun::star::table::XCellRange; +using ::com::sun::star::sheet::XFormulaTokens; +using ::com::sun::star::sheet::XArrayFormulaTokens; +using ::com::sun::star::text::XText; +using ::com::sun::star::xml::sax::XFastContextHandler; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +// record constants ----------------------------------------------------------- + +const sal_uInt16 OOBIN_CELL_SHOWPHONETIC = 0x0100; + +const sal_uInt8 OOBIN_DATATABLE_ROW = 0x01; +const sal_uInt8 OOBIN_DATATABLE_2D = 0x02; +const sal_uInt8 OOBIN_DATATABLE_REF1DEL = 0x04; +const sal_uInt8 OOBIN_DATATABLE_REF2DEL = 0x08; + +const sal_uInt16 OOBIN_ROW_THICKTOP = 0x0001; +const sal_uInt16 OOBIN_ROW_THICKBOTTOM = 0x0002; +const sal_uInt16 OOBIN_ROW_COLLAPSED = 0x0800; +const sal_uInt16 OOBIN_ROW_HIDDEN = 0x1000; +const sal_uInt16 OOBIN_ROW_CUSTOMHEIGHT = 0x2000; +const sal_uInt16 OOBIN_ROW_CUSTOMFORMAT = 0x4000; + +const sal_uInt8 BIFF_BOOLERR_BOOL = 0; +const sal_uInt8 BIFF_BOOLERR_ERROR = 1; + +const sal_uInt16 BIFF_DATATABLE_ROW = 0x0004; +const sal_uInt16 BIFF_DATATABLE_2D = 0x0008; +const sal_uInt16 BIFF_DATATABLE_REF1DEL = 0x0010; +const sal_uInt16 BIFF_DATATABLE_REF2DEL = 0x0020; + +const sal_uInt8 BIFF_FORMULA_RES_STRING = 0; /// Result is a string. +const sal_uInt8 BIFF_FORMULA_RES_BOOL = 1; /// Result is Boolean value. +const sal_uInt8 BIFF_FORMULA_RES_ERROR = 2; /// Result is error code. +const sal_uInt8 BIFF_FORMULA_RES_EMPTY = 3; /// Result is empty cell (BIFF8 only). +const sal_uInt16 BIFF_FORMULA_SHARED = 0x0008; /// Shared formula cell. + +const sal_uInt8 BIFF2_ROW_CUSTOMFORMAT = 0x01; +const sal_uInt16 BIFF_ROW_DEFAULTHEIGHT = 0x8000; +const sal_uInt16 BIFF_ROW_HEIGHTMASK = 0x7FFF; +const sal_uInt32 BIFF_ROW_COLLAPSED = 0x00000010; +const sal_uInt32 BIFF_ROW_HIDDEN = 0x00000020; +const sal_uInt32 BIFF_ROW_CUSTOMHEIGHT = 0x00000040; +const sal_uInt32 BIFF_ROW_CUSTOMFORMAT = 0x00000080; +const sal_uInt32 BIFF_ROW_THICKTOP = 0x10000000; +const sal_uInt32 BIFF_ROW_THICKBOTTOM = 0x20000000; +const sal_uInt32 BIFF_ROW_SHOWPHONETIC = 0x40000000; + +const sal_Int32 BIFF_XF_EXTENDED_IDS = 63; +const sal_uInt8 BIFF2_XF_MASK = 0x3F; + +// ---------------------------------------------------------------------------- + +/** Formula context for cell formulas. */ +class CellFormulaContext : public SimpleFormulaContext +{ +public: + explicit CellFormulaContext( + const Reference< XFormulaTokens >& rxTokens, + const CellAddress& rCellPos ); +}; + +CellFormulaContext::CellFormulaContext( const Reference< XFormulaTokens >& rxTokens, const CellAddress& rCellPos ) : + SimpleFormulaContext( rxTokens, false, false ) +{ + setBaseAddress( rCellPos ); +} + +// ---------------------------------------------------------------------------- + +/** Uses the XArrayFormulaTokens interface to set a token sequence. */ +class ArrayFormulaContext : public FormulaContext +{ +public: + explicit ArrayFormulaContext( + const Reference< XArrayFormulaTokens >& rxTokens, + const CellRangeAddress& rArrayRange ); + + virtual void setTokens( const ApiTokenSequence& rTokens ); + +private: + Reference< XArrayFormulaTokens > mxTokens; +}; + +ArrayFormulaContext::ArrayFormulaContext( + const Reference< XArrayFormulaTokens >& rxTokens, const CellRangeAddress& rArrayRange ) : + FormulaContext( false, false ), + mxTokens( rxTokens ) +{ + OSL_ENSURE( mxTokens.is(), "ArrayFormulaContext::ArrayFormulaContext - missing XArrayFormulaTokens interface" ); + setBaseAddress( CellAddress( rArrayRange.Sheet, rArrayRange.StartColumn, rArrayRange.StartRow ) ); +} + +void ArrayFormulaContext::setTokens( const ApiTokenSequence& rTokens ) +{ + mxTokens->setArrayTokens( rTokens ); +} + +// ---------------------------------------------------------------------------- + +} // namespace + +// ============================================================================ + +OoxSheetDataContext::OoxSheetDataContext( const OoxWorksheetFragmentBase& rFragment ) : + OoxWorksheetContextBase( rFragment ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxSheetDataContext::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( sheetData ): + return (nElement == XLS_TOKEN( row )); + case XLS_TOKEN( row ): + return (nElement == XLS_TOKEN( c )); + case XLS_TOKEN( c ): + return maCurrCell.mxCell.is() && + ((nElement == XLS_TOKEN( v )) || + (nElement == XLS_TOKEN( is )) || + (nElement == XLS_TOKEN( f ))); + } + return false; +} + +Reference< XFastContextHandler > OoxSheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) +{ + switch( nElement ) + { + case XLS_TOKEN( is ): + mxInlineStr.reset( new RichString( *this ) ); + return new OoxRichStringContext( *this, mxInlineStr ); + } + return this; +} + +void OoxSheetDataContext::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( row ): importRow( rAttribs ); break; + case XLS_TOKEN( c ): importCell( rAttribs ); break; + case XLS_TOKEN( f ): importFormula( rAttribs ); break; + } +} + +void OoxSheetDataContext::onEndElement( const OUString& rChars ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( v ): + maCurrCell.maValueStr = rChars; + maCurrCell.mbHasValueStr = true; + break; + + case XLS_TOKEN( f ): + if( maCurrCell.mxCell.is() ) try + { + switch( maCurrCell.mnFormulaType ) + { + case XML_normal: + if( rChars.getLength() > 0 ) + { + Reference< XFormulaTokens > xTokens( maCurrCell.mxCell, UNO_QUERY_THROW ); + CellFormulaContext aContext( xTokens, maCurrCell.maAddress ); + getFormulaParser().importFormula( aContext, rChars ); + } + break; + + case XML_array: + if( (maCurrCell.maFormulaRef.getLength() > 0) && (rChars.getLength() > 0) ) + { + CellRangeAddress aArrayRange; + Reference< XArrayFormulaTokens > xTokens( getCellRange( maCurrCell.maFormulaRef, &aArrayRange ), UNO_QUERY_THROW ); + ArrayFormulaContext aContext( xTokens, aArrayRange ); + getFormulaParser().importFormula( aContext, rChars ); + } + break; + + case XML_shared: + if( maCurrCell.mnSharedId >= 0 ) + { + if( rChars.getLength() > 0 ) + getSharedFormulas().importSharedFmla( rChars, maCurrCell.maFormulaRef, maCurrCell.mnSharedId, maCurrCell.maAddress ); + Reference< XFormulaTokens > xTokens( maCurrCell.mxCell, UNO_QUERY_THROW ); + ExtCellFormulaContext aContext( *this, xTokens, maCurrCell.maAddress ); + getSharedFormulas().setSharedFormulaCell( aContext, maCurrCell.mnSharedId ); + } + break; + + case XML_dataTable: + if( maCurrCell.maFormulaRef.getLength() > 0 ) + { + CellRangeAddress aTableRange; + if( getAddressConverter().convertToCellRange( aTableRange, maCurrCell.maFormulaRef, getSheetIndex(), true ) ) + setTableOperation( aTableRange, maTableData ); + } + break; + + default: + OSL_ENSURE( false, "OoxSheetDataContext::onEndElement - unknown formula type" ); + } + } + catch( Exception& ) + { + } + break; + + case XLS_TOKEN( c ): + if( maCurrCell.mxCell.is() ) + { + if( maCurrCell.mxCell->getType() == CellContentType_EMPTY ) + { + if( maCurrCell.mbHasValueStr ) + { + // implemented in WorksheetHelper class + setOoxCell( maCurrCell ); + } + else if( (maCurrCell.mnCellType == XML_inlineStr) && mxInlineStr.get() ) + { + // convert font settings + mxInlineStr->finalizeImport(); + // write string to cell + Reference< XText > xText( maCurrCell.mxCell, UNO_QUERY ); + if( xText.is() ) + mxInlineStr->convert( xText, maCurrCell.mnXfId ); + } + else + { + // empty cell, update cell type + maCurrCell.mnCellType = XML_TOKEN_INVALID; + } + } + + // store the cell formatting data + setCellFormat( maCurrCell ); + } + break; + } +} + +bool OoxSheetDataContext::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_SHEETDATA: + return (nRecId == OOBIN_ID_ROW); + case OOBIN_ID_ROW: + return (nRecId == OOBIN_ID_ARRAY) || + (nRecId == OOBIN_ID_CELL_BOOL) || + (nRecId == OOBIN_ID_CELL_BLANK) || + (nRecId == OOBIN_ID_CELL_DOUBLE) || + (nRecId == OOBIN_ID_CELL_ERROR) || + (nRecId == OOBIN_ID_CELL_RK) || + (nRecId == OOBIN_ID_CELL_RSTRING) || + (nRecId == OOBIN_ID_CELL_SI) || + (nRecId == OOBIN_ID_CELL_STRING) || + (nRecId == OOBIN_ID_DATATABLE) || + (nRecId == OOBIN_ID_FORMULA_BOOL) || + (nRecId == OOBIN_ID_FORMULA_DOUBLE) || + (nRecId == OOBIN_ID_FORMULA_ERROR) || + (nRecId == OOBIN_ID_FORMULA_STRING) || + (nRecId == OOBIN_ID_SHAREDFMLA); + } + return false; +} + +void OoxSheetDataContext::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_ARRAY: importArray( rStrm ); break; + case OOBIN_ID_CELL_BOOL: importCellBool( rStrm, false ); break; + case OOBIN_ID_CELL_BLANK: importCellBlank( rStrm ); break; + case OOBIN_ID_CELL_DOUBLE: importCellDouble( rStrm, false ); break; + case OOBIN_ID_CELL_ERROR: importCellError( rStrm, false ); break; + case OOBIN_ID_CELL_RK: importCellRk( rStrm ); break; + case OOBIN_ID_CELL_RSTRING: importCellRString( rStrm ); break; + case OOBIN_ID_CELL_SI: importCellSi( rStrm ); break; + case OOBIN_ID_CELL_STRING: importCellString( rStrm, false ); break; + case OOBIN_ID_DATATABLE: importDataTable( rStrm ); break; + case OOBIN_ID_FORMULA_BOOL: importCellBool( rStrm, true ); break; + case OOBIN_ID_FORMULA_DOUBLE: importCellDouble( rStrm, true ); break; + case OOBIN_ID_FORMULA_ERROR: importCellError( rStrm, true ); break; + case OOBIN_ID_FORMULA_STRING: importCellString( rStrm, true ); break; + case OOBIN_ID_ROW: importRow( rStrm ); break; + case OOBIN_ID_SHAREDFMLA: importSharedFmla( rStrm ); break; + } +} + +// private -------------------------------------------------------------------- + +void OoxSheetDataContext::importRow( const AttributeList& rAttribs ) +{ + OoxRowData aData; + aData.mnFirstRow = aData.mnLastRow = rAttribs.getInteger( XML_r, -1 ); + aData.mfHeight = rAttribs.getDouble( XML_ht, -1.0 ); + aData.mnXfId = rAttribs.getInteger( XML_s, -1 ); + aData.mnLevel = rAttribs.getInteger( XML_outlineLevel, 0 ); + aData.mbCustomHeight = rAttribs.getBool( XML_customHeight, false ); + aData.mbCustomFormat = rAttribs.getBool( XML_customFormat, false ); + aData.mbShowPhonetic = rAttribs.getBool( XML_ph, false ); + aData.mbHidden = rAttribs.getBool( XML_hidden, false ); + aData.mbCollapsed = rAttribs.getBool( XML_collapsed, false ); + aData.mbThickTop = rAttribs.getBool( XML_thickTop, false ); + aData.mbThickBottom = rAttribs.getBool( XML_thickBot, false ); + // set row properties in the current sheet + setRowData( aData ); +} + +void OoxSheetDataContext::importCell( const AttributeList& rAttribs ) +{ + maCurrCell.reset(); + maCurrCell.mxCell = getCell( rAttribs.getString( XML_r ), &maCurrCell.maAddress ); + maCurrCell.mnCellType = rAttribs.getToken( XML_t, XML_n ); + maCurrCell.mnXfId = rAttribs.getInteger( XML_s, -1 ); + maCurrCell.mbShowPhonetic = rAttribs.getBool( XML_ph, false ); + mxInlineStr.reset(); + + if( maCurrCell.mxCell.is() && getPivotTables().isOverlapping( maCurrCell.maAddress ) ) + // This cell overlaps a pivot table. Skip it. + maCurrCell.mxCell.clear(); +} + +void OoxSheetDataContext::importFormula( const AttributeList& rAttribs ) +{ + maCurrCell.maFormulaRef = rAttribs.getString( XML_ref ); + maCurrCell.mnFormulaType = rAttribs.getToken( XML_t, XML_normal ); + maCurrCell.mnSharedId = rAttribs.getInteger( XML_si, -1 ); + maTableData.maRef1 = rAttribs.getString( XML_r1 ); + maTableData.maRef2 = rAttribs.getString( XML_r2 ); + maTableData.mb2dTable = rAttribs.getBool( XML_dt2D, false ); + maTableData.mbRowTable = rAttribs.getBool( XML_dtr, false ); + maTableData.mbRef1Deleted = rAttribs.getBool( XML_del1, false ); + maTableData.mbRef2Deleted = rAttribs.getBool( XML_del2, false ); +} + +void OoxSheetDataContext::importCellHeader( RecordInputStream& rStrm ) +{ + maCurrCell.reset(); + sal_uInt16 nXfId, nFlags; + rStrm >> maCurrPos.mnCol >> nXfId >> nFlags; + + maCurrCell.mxCell = getCell( maCurrPos, &maCurrCell.maAddress ); + maCurrCell.mnXfId = nXfId; + maCurrCell.mbShowPhonetic = getFlag( nFlags, OOBIN_CELL_SHOWPHONETIC ); +} + +void OoxSheetDataContext::importCellBool( RecordInputStream& rStrm, bool bFormula ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_b; + if( maCurrCell.mxCell.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + { + bool bValue = rStrm.readuInt8() != 0; + if( bFormula ) + { + importCellFormula( rStrm ); + } + else + { + setBooleanCell( maCurrCell.mxCell, bValue ); + // #108770# set 'Standard' number format for all Boolean cells + maCurrCell.mnNumFmtId = 0; + } + } + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellBlank( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellDouble( RecordInputStream& rStrm, bool bFormula ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_n; + if( maCurrCell.mxCell.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + { + double fValue = rStrm.readDouble(); + if( bFormula ) + importCellFormula( rStrm ); + else + maCurrCell.mxCell->setValue( fValue ); + } + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellError( RecordInputStream& rStrm, bool bFormula ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_e; + if( maCurrCell.mxCell.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + { + sal_uInt8 nErrorCode = rStrm.readuInt8(); + if( bFormula ) + importCellFormula( rStrm ); + else + setErrorCell( maCurrCell.mxCell, nErrorCode ); + } + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellRk( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_n; + if( maCurrCell.mxCell.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + maCurrCell.mxCell->setValue( BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) ); + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellRString( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_inlineStr; + Reference< XText > xText( maCurrCell.mxCell, UNO_QUERY ); + if( xText.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + { + RichString aString( *this ); + aString.importString( rStrm, true ); + aString.finalizeImport(); + aString.convert( xText, maCurrCell.mnXfId ); + } + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellSi( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_s; + if( maCurrCell.mxCell.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + setSharedStringCell( maCurrCell.mxCell, rStrm.readInt32(), maCurrCell.mnXfId ); + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellString( RecordInputStream& rStrm, bool bFormula ) +{ + importCellHeader( rStrm ); + maCurrCell.mnCellType = XML_inlineStr; + Reference< XText > xText( maCurrCell.mxCell, UNO_QUERY ); + if( xText.is() && (maCurrCell.mxCell->getType() == CellContentType_EMPTY) ) + { + RichString aString( *this ); + aString.importString( rStrm, false ); + aString.finalizeImport(); + if( bFormula ) + importCellFormula( rStrm ); + else + aString.convert( xText, maCurrCell.mnXfId ); + } + setCellFormat( maCurrCell ); +} + +void OoxSheetDataContext::importCellFormula( RecordInputStream& rStrm ) +{ + rStrm.skip( 2 ); + Reference< XFormulaTokens > xTokens( maCurrCell.mxCell, UNO_QUERY ); + if( xTokens.is() ) + { + ExtCellFormulaContext aContext( *this, xTokens, maCurrCell.maAddress ); + getFormulaParser().importFormula( aContext, rStrm ); + } +} + +void OoxSheetDataContext::importRow( RecordInputStream& rStrm ) +{ + OoxRowData aData; + + sal_uInt16 nHeight, nFlags; + rStrm >> maCurrPos.mnRow >> aData.mnXfId >> nHeight >> nFlags; + + // row index is 0-based in OOBIN, but OoxRowData expects 1-based + aData.mnFirstRow = aData.mnLastRow = maCurrPos.mnRow + 1; + // row height is in twips in OOBIN, convert to points + aData.mfHeight = nHeight / 20.0; + aData.mnLevel = extractValue< sal_Int32 >( nFlags, 8, 3 ); + aData.mbCustomHeight = getFlag( nFlags, OOBIN_ROW_CUSTOMHEIGHT ); + aData.mbCustomFormat = getFlag( nFlags, OOBIN_ROW_CUSTOMFORMAT ); + // 'show phonetic' missing in OOBIN (bug in Excel 2007) + aData.mbHidden = getFlag( nFlags, OOBIN_ROW_HIDDEN ); + aData.mbCollapsed = getFlag( nFlags, OOBIN_ROW_COLLAPSED ); + aData.mbThickTop = getFlag( nFlags, OOBIN_ROW_THICKTOP ); + aData.mbThickBottom = getFlag( nFlags, OOBIN_ROW_THICKBOTTOM ); + // set row properties in the current sheet + setRowData( aData ); +} + +void OoxSheetDataContext::importArray( RecordInputStream& rStrm ) +{ + BinRange aRange; + rStrm >> aRange; + CellRangeAddress aArrayRange; + Reference< XCellRange > xRange = getCellRange( aRange, &aArrayRange ); + Reference< XArrayFormulaTokens > xTokens( xRange, UNO_QUERY ); + if( xRange.is() && xTokens.is() ) + { + rStrm.skip( 1 ); + ArrayFormulaContext aContext( xTokens, aArrayRange ); + getFormulaParser().importFormula( aContext, rStrm ); + } +} + +void OoxSheetDataContext::importSharedFmla( RecordInputStream& rStrm ) +{ + getSharedFormulas().importSharedFmla( rStrm, maCurrCell.maAddress ); +} + +void OoxSheetDataContext::importDataTable( RecordInputStream& rStrm ) +{ + BinRange aRange; + rStrm >> aRange; + CellRangeAddress aTableRange; + if( getAddressConverter().convertToCellRange( aTableRange, aRange, getSheetIndex(), true ) ) + { + OoxDataTableData aTableData; + BinAddress aRef1, aRef2; + sal_uInt8 nFlags; + rStrm >> aRef1 >> aRef2 >> nFlags; + aTableData.maRef1 = FormulaProcessorBase::generateAddress2dString( aRef1, false ); + aTableData.maRef2 = FormulaProcessorBase::generateAddress2dString( aRef2, false ); + aTableData.mbRowTable = getFlag( nFlags, OOBIN_DATATABLE_ROW ); + aTableData.mb2dTable = getFlag( nFlags, OOBIN_DATATABLE_2D ); + aTableData.mbRef1Deleted = getFlag( nFlags, OOBIN_DATATABLE_REF1DEL ); + aTableData.mbRef2Deleted = getFlag( nFlags, OOBIN_DATATABLE_REF2DEL ); + setTableOperation( aTableRange, aTableData ); + } +} + +// ============================================================================ + +OoxExternalSheetDataContext::OoxExternalSheetDataContext( + const OoxWorkbookFragmentBase& rFragment, WorksheetType eSheetType, sal_Int32 nSheet ) : + OoxWorksheetContextBase( rFragment, ISegmentProgressBarRef(), eSheetType, nSheet ) +{ +} + +// oox.xls.ContextHelper interface -------------------------------------------- + +bool OoxExternalSheetDataContext::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( sheetData ): + return (nElement == XLS_TOKEN( row )); + case XLS_TOKEN( row ): + return (nElement == XLS_TOKEN( cell )); + case XLS_TOKEN( cell ): + return (nElement == XLS_TOKEN( v )) && maCurrCell.mxCell.is(); + } + return false; +} + +void OoxExternalSheetDataContext::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( cell ): + importCell( rAttribs ); + break; + } +} + +void OoxExternalSheetDataContext::onEndElement( const OUString& rChars ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( v ): + maCurrCell.maValueStr = rChars; + maCurrCell.mbHasValueStr = true; + break; + + case XLS_TOKEN( cell ): + if( maCurrCell.mxCell.is() ) + setOoxCell( maCurrCell, true ); + break; + } +} + +bool OoxExternalSheetDataContext::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_EXTSHEETDATA: + return (nRecId == OOBIN_ID_EXTROW); + case OOBIN_ID_EXTROW: + return (nRecId == OOBIN_ID_EXTCELL_BOOL) || + (nRecId == OOBIN_ID_EXTCELL_DOUBLE) || + (nRecId == OOBIN_ID_EXTCELL_ERROR) || + (nRecId == OOBIN_ID_EXTCELL_STRING); + } + return false; +} + +void OoxExternalSheetDataContext::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_EXTCELL_BOOL: importExtCellBool( rStrm ); break; + case OOBIN_ID_EXTCELL_DOUBLE: importExtCellDouble( rStrm ); break; + case OOBIN_ID_EXTCELL_ERROR: importExtCellError( rStrm ); break; + case OOBIN_ID_EXTCELL_STRING: importExtCellString( rStrm ); break; + case OOBIN_ID_EXTROW: rStrm >> maCurrPos.mnRow; break; + } +} + +// private -------------------------------------------------------------------- + +void OoxExternalSheetDataContext::importCell( const AttributeList& rAttribs ) +{ + maCurrCell.reset(); + maCurrCell.mxCell = getCell( rAttribs.getString( XML_r ), &maCurrCell.maAddress ); + maCurrCell.mnCellType = rAttribs.getToken( XML_t, XML_n ); +} + +void OoxExternalSheetDataContext::importCellHeader( RecordInputStream& rStrm ) +{ + maCurrCell.reset(); + rStrm >> maCurrPos.mnCol; + maCurrCell.mxCell = getCell( maCurrPos, &maCurrCell.maAddress ); +} + +void OoxExternalSheetDataContext::importExtCellBool( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + if( maCurrCell.mxCell.is() ) + setBooleanCell( maCurrCell.mxCell, rStrm.readuInt8() != 0 ); +} + +void OoxExternalSheetDataContext::importExtCellDouble( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + if( maCurrCell.mxCell.is() ) + maCurrCell.mxCell->setValue( rStrm.readDouble() ); +} + +void OoxExternalSheetDataContext::importExtCellError( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + if( maCurrCell.mxCell.is() ) + setErrorCell( maCurrCell.mxCell, rStrm.readuInt8() ); +} + +void OoxExternalSheetDataContext::importExtCellString( RecordInputStream& rStrm ) +{ + importCellHeader( rStrm ); + if( maCurrCell.mxCell.is() ) + setStringCell( maCurrCell.mxCell, rStrm.readString(), true ); +} + +// ============================================================================ +// ============================================================================ + +BiffSheetDataContext::BiffSheetDataContext( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ), + mnBiff2XfId( 0 ) +{ + mnArrayIgnoreSize = (getBiff() == BIFF2) ? 1 : ((getBiff() <= BIFF4) ? 2 : 6); + switch( getBiff() ) + { + case BIFF2: + mnFormulaIgnoreSize = 9; // double formula result, 1 byte flags + mnArrayIgnoreSize = 1; // recalc-always flag + break; + case BIFF3: + case BIFF4: + mnFormulaIgnoreSize = 10; // double formula result, 2 byte flags + mnArrayIgnoreSize = 2; // 2 byte flags + break; + case BIFF5: + case BIFF8: + mnFormulaIgnoreSize = 14; // double formula result, 2 byte flags, 4 bytes nothing + mnArrayIgnoreSize = 6; // 2 byte flags, 4 bytes nothing + break; + case BIFF_UNKNOWN: break; + } +} + +void BiffSheetDataContext::importRecord( BiffInputStream& rStrm ) +{ + sal_uInt16 nRecId = rStrm.getRecId(); + switch( nRecId ) + { + // records in all BIFF versions + case BIFF2_ID_ARRAY: // #i72713# + case BIFF3_ID_ARRAY: importArray( rStrm ); break; + case BIFF2_ID_BLANK: + case BIFF3_ID_BLANK: importBlank( rStrm ); break; + case BIFF2_ID_BOOLERR: + case BIFF3_ID_BOOLERR: importBoolErr( rStrm ); break; + case BIFF2_ID_INTEGER: importInteger( rStrm ); break; + case BIFF_ID_IXFE: rStrm >> mnBiff2XfId; break; + case BIFF2_ID_LABEL: + case BIFF3_ID_LABEL: importLabel( rStrm ); break; + case BIFF2_ID_NUMBER: + case BIFF3_ID_NUMBER: importNumber( rStrm ); break; + case BIFF_ID_RK: importRk( rStrm ); break; + + // BIFF specific records + default: switch( getBiff() ) + { + case BIFF2: switch( nRecId ) + { + case BIFF2_ID_DATATABLE: importDataTable( rStrm ); break; + case BIFF2_ID_DATATABLE2: importDataTable( rStrm ); break; + case BIFF2_ID_FORMULA: importFormula( rStrm ); break; + case BIFF2_ID_ROW: importRow( rStrm ); break; + } + break; + + case BIFF3: switch( nRecId ) + { + case BIFF3_ID_DATATABLE: importDataTable( rStrm ); break; + case BIFF3_ID_FORMULA: importFormula( rStrm ); break; + case BIFF3_ID_ROW: importRow( rStrm ); break; + } + break; + + case BIFF4: switch( nRecId ) + { + case BIFF3_ID_DATATABLE: importDataTable( rStrm ); break; + case BIFF4_ID_FORMULA: importFormula( rStrm ); break; + case BIFF3_ID_ROW: importRow( rStrm ); break; + } + break; + + case BIFF5: switch( nRecId ) + { + case BIFF3_ID_DATATABLE: importDataTable( rStrm ); break; + case BIFF3_ID_FORMULA: + case BIFF4_ID_FORMULA: + case BIFF5_ID_FORMULA: importFormula( rStrm ); break; + case BIFF_ID_MULTBLANK: importMultBlank( rStrm ); break; + case BIFF_ID_MULTRK: importMultRk( rStrm ); break; + case BIFF3_ID_ROW: importRow( rStrm ); break; + case BIFF_ID_RSTRING: importLabel( rStrm ); break; + case BIFF_ID_SHAREDFMLA: importSharedFmla( rStrm ); break; + } + break; + + case BIFF8: switch( nRecId ) + { + case BIFF3_ID_DATATABLE: importDataTable( rStrm ); break; + case BIFF3_ID_FORMULA: + case BIFF4_ID_FORMULA: + case BIFF5_ID_FORMULA: importFormula( rStrm ); break; + case BIFF_ID_LABELSST: importLabelSst( rStrm ); break; + case BIFF_ID_MULTBLANK: importMultBlank( rStrm ); break; + case BIFF_ID_MULTRK: importMultRk( rStrm ); break; + case BIFF3_ID_ROW: importRow( rStrm ); break; + case BIFF_ID_RSTRING: importLabel( rStrm ); break; + case BIFF_ID_SHAREDFMLA: importSharedFmla( rStrm ); break; + } + break; + + case BIFF_UNKNOWN: break; + } + } +} + +// private -------------------------------------------------------------------- + +void BiffSheetDataContext::setCurrCell( const BinAddress& rAddr ) +{ + maCurrCell.reset(); + maCurrCell.mxCell = getCell( rAddr, &maCurrCell.maAddress ); +} + +void BiffSheetDataContext::importXfId( BiffInputStream& rStrm, bool bBiff2 ) +{ + if( bBiff2 ) + { + sal_uInt8 nBiff2XfId; + rStrm >> nBiff2XfId; + rStrm.skip( 2 ); + maCurrCell.mnXfId = nBiff2XfId & BIFF2_XF_MASK; + if( maCurrCell.mnXfId == BIFF_XF_EXTENDED_IDS ) + maCurrCell.mnXfId = mnBiff2XfId; + } + else + { + maCurrCell.mnXfId = rStrm.readuInt16(); + } +} + +void BiffSheetDataContext::importCellHeader( BiffInputStream& rStrm, bool bBiff2 ) +{ + BinAddress aAddr; + rStrm >> aAddr; + setCurrCell( aAddr ); + importXfId( rStrm, bBiff2 ); +} + +void BiffSheetDataContext::importBlank( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, rStrm.getRecId() == BIFF2_ID_BLANK ); + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importBoolErr( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, rStrm.getRecId() == BIFF2_ID_BOOLERR ); + if( maCurrCell.mxCell.is() ) + { + sal_uInt8 nValue, nType; + rStrm >> nValue >> nType; + switch( nType ) + { + case BIFF_BOOLERR_BOOL: + maCurrCell.mnCellType = XML_b; + setBooleanCell( maCurrCell.mxCell, nValue != 0 ); + // #108770# set 'Standard' number format for all Boolean cells + maCurrCell.mnNumFmtId = 0; + break; + case BIFF_BOOLERR_ERROR: + maCurrCell.mnCellType = XML_e; + setErrorCell( maCurrCell.mxCell, nValue ); + break; + default: + OSL_ENSURE( false, "BiffSheetDataContext::importBoolErr - unknown cell type" ); + } + } + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importFormula( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, getBiff() == BIFF2 ); + maCurrCell.mnCellType = XML_n; + Reference< XFormulaTokens > xTokens( maCurrCell.mxCell, UNO_QUERY ); + if( xTokens.is() ) + { + rStrm.skip( mnFormulaIgnoreSize ); + ExtCellFormulaContext aContext( *this, xTokens, maCurrCell.maAddress ); + getFormulaParser().importFormula( aContext, rStrm ); + } + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importInteger( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, true ); + maCurrCell.mnCellType = XML_n; + if( maCurrCell.mxCell.is() ) + maCurrCell.mxCell->setValue( rStrm.readuInt16() ); + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importLabel( BiffInputStream& rStrm ) +{ + bool bBiff2Xf = rStrm.getRecId() == BIFF2_ID_LABEL; + importCellHeader( rStrm, bBiff2Xf ); + maCurrCell.mnCellType = XML_inlineStr; + Reference< XText > xText( maCurrCell.mxCell, UNO_QUERY ); + if( xText.is() ) + { + /* the deep secrets of BIFF type and record identifier... + record id BIFF XF type String type + 0x0004 2-7 3 byte 8-bit length, byte string + 0x0004 8 3 byte 16-bit length, unicode string + 0x0204 2-7 2 byte 16-bit length, byte string + 0x0204 8 2 byte 16-bit length, unicode string */ + + RichString aString( *this ); + if( getBiff() == BIFF8 ) + { + aString.importUniString( rStrm ); + } + else + { + // #i63105# use text encoding from FONT record + rtl_TextEncoding eTextEnc = getTextEncoding(); + if( const Font* pFont = getStyles().getFontFromCellXf( maCurrCell.mnXfId ).get() ) + eTextEnc = pFont->getFontEncoding(); + BiffStringFlags nFlags = bBiff2Xf ? BIFF_STR_8BITLENGTH : BIFF_STR_DEFAULT; + setFlag( nFlags, BIFF_STR_EXTRAFONTS, rStrm.getRecId() == BIFF_ID_RSTRING ); + aString.importByteString( rStrm, eTextEnc, nFlags ); + } + aString.finalizeImport(); + aString.convert( xText, maCurrCell.mnXfId ); + } + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importLabelSst( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, false ); + maCurrCell.mnCellType = XML_s; + if( maCurrCell.mxCell.is() ) + setSharedStringCell( maCurrCell.mxCell, rStrm.readInt32(), maCurrCell.mnXfId ); + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importMultBlank( BiffInputStream& rStrm ) +{ + BinAddress aAddr; + for( rStrm >> aAddr; rStrm.getRecLeft() > 2; ++aAddr.mnCol ) + { + setCurrCell( aAddr ); + importXfId( rStrm, false ); + setCellFormat( maCurrCell ); + } +} + +void BiffSheetDataContext::importMultRk( BiffInputStream& rStrm ) +{ + BinAddress aAddr; + for( rStrm >> aAddr; rStrm.getRecLeft() > 2; ++aAddr.mnCol ) + { + setCurrCell( aAddr ); + maCurrCell.mnCellType = XML_n; + importXfId( rStrm, false ); + sal_Int32 nRkValue = rStrm.readInt32(); + if( maCurrCell.mxCell.is() ) + maCurrCell.mxCell->setValue( BiffHelper::calcDoubleFromRk( nRkValue ) ); + setCellFormat( maCurrCell ); + } +} + +void BiffSheetDataContext::importNumber( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, rStrm.getRecId() == BIFF2_ID_NUMBER ); + maCurrCell.mnCellType = XML_n; + if( maCurrCell.mxCell.is() ) + maCurrCell.mxCell->setValue( rStrm.readDouble() ); + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importRk( BiffInputStream& rStrm ) +{ + importCellHeader( rStrm, false ); + maCurrCell.mnCellType = XML_n; + if( maCurrCell.mxCell.is() ) + maCurrCell.mxCell->setValue( BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) ); + setCellFormat( maCurrCell ); +} + +void BiffSheetDataContext::importRow( BiffInputStream& rStrm ) +{ + OoxRowData aData; + + sal_uInt16 nRow, nHeight; + rStrm >> nRow; + rStrm.skip( 4 ); + rStrm >> nHeight; + if( getBiff() == BIFF2 ) + { + aData.mbCustomFormat = rStrm.skip( 2 ).readuInt8() == BIFF2_ROW_CUSTOMFORMAT; + if( aData.mbCustomFormat ) + aData.mnXfId = rStrm.skip( 5 ).readuInt16(); + } + else + { + sal_uInt32 nFlags = rStrm.skip( 4 ).readuInt32(); + aData.mnXfId = extractValue< sal_Int32 >( nFlags, 16, 12 ); + aData.mnLevel = extractValue< sal_Int32 >( nFlags, 0, 3 ); + aData.mbCustomFormat = getFlag( nFlags, BIFF_ROW_CUSTOMFORMAT ); + aData.mbCustomHeight = getFlag( nFlags, BIFF_ROW_CUSTOMHEIGHT ); + aData.mbShowPhonetic = getFlag( nFlags, BIFF_ROW_SHOWPHONETIC ); + aData.mbHidden = getFlag( nFlags, BIFF_ROW_HIDDEN ); + aData.mbCollapsed = getFlag( nFlags, BIFF_ROW_COLLAPSED ); + aData.mbThickTop = getFlag( nFlags, BIFF_ROW_THICKTOP ); + aData.mbThickBottom = getFlag( nFlags, BIFF_ROW_THICKBOTTOM ); + } + + // row index is 0-based in BIFF, but OoxRowData expects 1-based + aData.mnFirstRow = aData.mnLastRow = nRow + 1; + // row height is in twips in BIFF, convert to points + aData.mfHeight = (nHeight & BIFF_ROW_HEIGHTMASK) / 20.0; + // set row properties in the current sheet + setRowData( aData ); +} + +void BiffSheetDataContext::importArray( BiffInputStream& rStrm ) +{ + BinRange aRange; + aRange.read( rStrm, false ); // columns always 8-bit + CellRangeAddress aArrayRange; + Reference< XCellRange > xRange = getCellRange( aRange, &aArrayRange ); + Reference< XArrayFormulaTokens > xTokens( xRange, UNO_QUERY ); + if( xRange.is() && xTokens.is() ) + { + rStrm.skip( mnArrayIgnoreSize ); + ArrayFormulaContext aContext( xTokens, aArrayRange ); + getFormulaParser().importFormula( aContext, rStrm ); + } +} + +void BiffSheetDataContext::importSharedFmla( BiffInputStream& rStrm ) +{ + getSharedFormulas().importSharedFmla( rStrm, maCurrCell.maAddress ); +} + +void BiffSheetDataContext::importDataTable( BiffInputStream& rStrm ) +{ + BinRange aRange; + aRange.read( rStrm, false ); // columns always 8-bit + CellRangeAddress aTableRange; + if( getAddressConverter().convertToCellRange( aTableRange, aRange, getSheetIndex(), true ) ) + { + OoxDataTableData aTableData; + BinAddress aRef1, aRef2; + switch( rStrm.getRecId() ) + { + case BIFF2_ID_DATATABLE: + rStrm.skip( 1 ); + aTableData.mbRowTable = rStrm.readuInt8() != 0; + aTableData.mb2dTable = false; + rStrm >> aRef1; + break; + case BIFF2_ID_DATATABLE2: + rStrm.skip( 2 ); + aTableData.mb2dTable = true; + rStrm >> aRef1 >> aRef2; + break; + case BIFF3_ID_DATATABLE: + { + sal_uInt16 nFlags; + rStrm >> nFlags >> aRef1 >> aRef2; + aTableData.mbRowTable = getFlag( nFlags, BIFF_DATATABLE_ROW ); + aTableData.mb2dTable = getFlag( nFlags, BIFF_DATATABLE_2D ); + aTableData.mbRef1Deleted = getFlag( nFlags, BIFF_DATATABLE_REF1DEL ); + aTableData.mbRef2Deleted = getFlag( nFlags, BIFF_DATATABLE_REF2DEL ); + } + break; + default: + OSL_ENSURE( false, "BiffSheetDataContext::importDataTable - unknown record id" ); + } + aTableData.maRef1 = FormulaProcessorBase::generateAddress2dString( aRef1, false ); + aTableData.maRef2 = FormulaProcessorBase::generateAddress2dString( aRef2, false ); + setTableOperation( aTableRange, aTableData ); + } +} + +// ============================================================================ + +BiffExternalSheetDataContext::BiffExternalSheetDataContext( + const WorkbookHelper& rHelper, WorksheetType eSheetType, sal_Int32 nSheet ) : + WorksheetHelperRoot( rHelper, ISegmentProgressBarRef(), eSheetType, nSheet ) +{ +} + +void BiffExternalSheetDataContext::importCrn( BiffInputStream& rStrm ) +{ + sal_uInt8 nCol2, nCol1; + sal_uInt16 nRow; + rStrm >> nCol2 >> nCol1 >> nRow; + bool bLoop = true; + for( BinAddress aAddr( nCol1, nRow ); bLoop && rStrm.isValid() && (aAddr.mnCol <= nCol2); ++aAddr.mnCol ) + { + Reference< XCell > xCell = getCell( aAddr ); + bLoop = xCell.is(); + if( bLoop ) switch( rStrm.readuInt8() ) + { + case BIFF_DATATYPE_EMPTY: + rStrm.skip( 8 ); + setEmptyStringCell( xCell ); + break; + case BIFF_DATATYPE_DOUBLE: + xCell->setValue( rStrm.readDouble() ); + break; + case BIFF_DATATYPE_STRING: + { + OUString aText = (getBiff() == BIFF8) ? rStrm.readUniString() : rStrm.readByteString( false, getTextEncoding() ); + setStringCell( xCell, aText, true ); + } + break; + case BIFF_DATATYPE_BOOL: + setBooleanCell( xCell, rStrm.readuInt8() != 0 ); + rStrm.skip( 7 ); + break; + case BIFF_DATATYPE_ERROR: + setErrorCell( xCell, rStrm.readuInt8() ); + rStrm.skip( 7 ); + break; + default: + OSL_ENSURE( false, "BiffExternalSheetDataContext::importCrn - unknown data type" ); + bLoop = false; + } + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/stylesbuffer.cxx b/oox/source/xls/stylesbuffer.cxx new file mode 100644 index 000000000000..66b1ea9e8035 --- /dev/null +++ b/oox/source/xls/stylesbuffer.cxx @@ -0,0 +1,3214 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stylesbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/stylesbuffer.hxx" +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/FontFamily.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/XFont2.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/text/XText.hpp> +#include <rtl/tencinfo.h> +#include <rtl/ustrbuf.hxx> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/condformatbuffer.hxx" +#include "oox/xls/ooxtokens.hxx" +#include "oox/xls/themebuffer.hxx" +#include "oox/xls/unitconverter.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::awt::FontDescriptor; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::XFont2; +using ::com::sun::star::table::BorderLine; +using ::com::sun::star::text::XText; +using ::com::sun::star::style::XStyle; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +// OOXML constants ------------------------------------------------------------ + +// OOX predefined color indexes (also used in BIFF3-BIFF8) +const sal_Int32 OOX_COLOR_USEROFFSET = 0; /// First user defined color in palette (OOX). +const sal_Int32 BIFF_COLOR_USEROFFSET = 8; /// First user defined color in palette (BIFF). + +// OOX font family (also used in BIFF) +const sal_Int32 OOX_FONTFAMILY_NONE = 0; +const sal_Int32 OOX_FONTFAMILY_ROMAN = 1; +const sal_Int32 OOX_FONTFAMILY_SWISS = 2; +const sal_Int32 OOX_FONTFAMILY_MODERN = 3; +const sal_Int32 OOX_FONTFAMILY_SCRIPT = 4; +const sal_Int32 OOX_FONTFAMILY_DECORATIVE = 5; + +// OOX font charset (also used in BIFF) +const sal_Int32 OOX_FONTCHARSET_UNUSED = -1; +const sal_Int32 OOX_FONTCHARSET_ANSI = 0; + +// OOX cell text direction (also used in BIFF) +const sal_Int32 OOX_XF_TEXTDIR_CONTEXT = 0; +const sal_Int32 OOX_XF_TEXTDIR_LTR = 1; +const sal_Int32 OOX_XF_TEXTDIR_RTL = 2; + +// OOX cell rotation (also used in BIFF) +const sal_Int32 OOX_XF_ROTATION_NONE = 0; +const sal_Int32 OOX_XF_ROTATION_90CCW = 90; +const sal_Int32 OOX_XF_ROTATION_90CW = 180; +const sal_Int32 OOX_XF_ROTATION_STACKED = 255; + +// OOX cell indentation +const sal_Int32 OOX_XF_INDENT_NONE = 0; + +// OOX built-in cell styles (also used in BIFF) +const sal_Int32 OOX_STYLE_NORMAL = 0; /// Default cell style. +const sal_Int32 OOX_STYLE_ROWLEVEL = 1; /// RowLevel_x cell style. +const sal_Int32 OOX_STYLE_COLLEVEL = 2; /// ColLevel_x cell style. + +const sal_Int32 OOX_STYLE_LEVELCOUNT = 7; /// Number of outline level styles. + +// OOBIN constants ------------------------------------------------------------ + +// OOBIN color types +const sal_uInt8 OOBIN_COLOR_AUTO = 1; +const sal_uInt8 OOBIN_COLOR_INDEXED = 3; +const sal_uInt8 OOBIN_COLOR_RGB = 5; +const sal_uInt8 OOBIN_COLOR_THEME = 7; + +// OOBIN diagonal borders +const sal_uInt8 OOBIN_BORDER_DIAG_TLBR = 0x01; /// Top-left to bottom-right. +const sal_uInt8 OOBIN_BORDER_DIAG_BLTR = 0x02; /// Bottom-left to top-right. + +// OOBIN gradient fill +const sal_Int32 OOBIN_FILL_GRADIENT = 40; + +// OOBIN XF flags +const sal_uInt32 OOBIN_XF_WRAPTEXT = 0x00400000; +const sal_uInt32 OOBIN_XF_JUSTLASTLINE = 0x00800000; +const sal_uInt32 OOBIN_XF_SHRINK = 0x01000000; +const sal_uInt32 OOBIN_XF_LOCKED = 0x10000000; +const sal_uInt32 OOBIN_XF_HIDDEN = 0x20000000; + +// OOBIN XF attribute used flags +const sal_uInt16 OOBIN_XF_NUMFMT_USED = 0x0001; +const sal_uInt16 OOBIN_XF_FONT_USED = 0x0002; +const sal_uInt16 OOBIN_XF_ALIGN_USED = 0x0004; +const sal_uInt16 OOBIN_XF_BORDER_USED = 0x0008; +const sal_uInt16 OOBIN_XF_AREA_USED = 0x0010; +const sal_uInt16 OOBIN_XF_PROT_USED = 0x0020; + +// OOBIN DXF constants +const sal_uInt16 OOBIN_DXF_FILL_PATTERN = 0; +const sal_uInt16 OOBIN_DXF_FILL_FGCOLOR = 1; +const sal_uInt16 OOBIN_DXF_FILL_BGCOLOR = 2; +const sal_uInt16 OOBIN_DXF_FILL_GRADIENT = 3; +const sal_uInt16 OOBIN_DXF_FILL_STOP = 4; +const sal_uInt16 OOBIN_DXF_FONT_COLOR = 5; +const sal_uInt16 OOBIN_DXF_BORDER_TOP = 6; +const sal_uInt16 OOBIN_DXF_BORDER_BOTTOM = 7; +const sal_uInt16 OOBIN_DXF_BORDER_LEFT = 8; +const sal_uInt16 OOBIN_DXF_BORDER_RIGHT = 9; +const sal_uInt16 OOBIN_DXF_BORDER_DIAG = 10; +const sal_uInt16 OOBIN_DXF_BORDER_VERT = 11; +const sal_uInt16 OOBIN_DXF_BORDER_HOR = 12; +const sal_uInt16 OOBIN_DXF_BORDER_DIAGUP = 13; +const sal_uInt16 OOBIN_DXF_BORDER_DIAGDOWN = 14; +const sal_uInt16 OOBIN_DXF_FONT_NAME = 24; +const sal_uInt16 OOBIN_DXF_FONT_WEIGHT = 25; +const sal_uInt16 OOBIN_DXF_FONT_UNDERLINE = 26; +const sal_uInt16 OOBIN_DXF_FONT_ESCAPEMENT = 27; +const sal_uInt16 OOBIN_DXF_FONT_ITALIC = 28; +const sal_uInt16 OOBIN_DXF_FONT_STRIKE = 29; +const sal_uInt16 OOBIN_DXF_FONT_OUTLINE = 30; +const sal_uInt16 OOBIN_DXF_FONT_SHADOW = 31; +const sal_uInt16 OOBIN_DXF_FONT_CONDENSE = 32; +const sal_uInt16 OOBIN_DXF_FONT_EXTEND = 33; +const sal_uInt16 OOBIN_DXF_FONT_HEIGHT = 36; +const sal_uInt16 OOBIN_DXF_FONT_SCHEME = 37; +const sal_uInt16 OOBIN_DXF_NUMFMT_CODE = 38; +const sal_uInt16 OOBIN_DXF_NUMFMT_ID = 41; + +// OOBIN CELLSTYLE flags +const sal_uInt16 OOBIN_CELLSTYLE_BUILTIN = 0x0001; +const sal_uInt16 OOBIN_CELLSTYLE_HIDDEN = 0x0002; +const sal_uInt16 OOBIN_CELLSTYLE_CUSTOM = 0x0004; + +// OOBIN and BIFF constants --------------------------------------------------- + +// BIFF predefined color indexes +const sal_uInt16 BIFF2_COLOR_BLACK = 0; /// Black (text) in BIFF2. +const sal_uInt16 BIFF2_COLOR_WHITE = 1; /// White (background) in BIFF2. + +// BIFF font flags, also used in OOBIN +const sal_uInt16 BIFF_FONTFLAG_BOLD = 0x0001; +const sal_uInt16 BIFF_FONTFLAG_ITALIC = 0x0002; +const sal_uInt16 BIFF_FONTFLAG_UNDERLINE = 0x0004; +const sal_uInt16 BIFF_FONTFLAG_STRIKEOUT = 0x0008; +const sal_uInt16 BIFF_FONTFLAG_OUTLINE = 0x0010; +const sal_uInt16 BIFF_FONTFLAG_SHADOW = 0x0020; +const sal_uInt16 BIFF_FONTFLAG_CONDENSE = 0x0040; + +// BIFF font weight +const sal_uInt16 BIFF_FONTWEIGHT_BOLD = 450; + +// BIFF font underline, also used in OOBIN +const sal_uInt8 BIFF_FONTUNDERL_NONE = 0; +const sal_uInt8 BIFF_FONTUNDERL_SINGLE = 1; +const sal_uInt8 BIFF_FONTUNDERL_DOUBLE = 2; +const sal_uInt8 BIFF_FONTUNDERL_SINGLE_ACC = 33; +const sal_uInt8 BIFF_FONTUNDERL_DOUBLE_ACC = 34; + +// BIFF XF flags +const sal_uInt16 BIFF_XF_LOCKED = 0x0001; +const sal_uInt16 BIFF_XF_HIDDEN = 0x0002; +const sal_uInt16 BIFF_XF_STYLE = 0x0004; +const sal_uInt16 BIFF_XF_STYLEPARENT = 0x0FFF; /// Syles don't have a parent. +const sal_uInt16 BIFF_XF_WRAPTEXT = 0x0008; /// Automatic line break. +const sal_uInt16 BIFF_XF_JUSTLASTLINE = 0x0080; +const sal_uInt16 BIFF_XF_SHRINK = 0x0010; /// Shrink to fit into cell. +const sal_uInt16 BIFF_XF_MERGE = 0x0020; + +// BIFF XF attribute used flags +const sal_uInt8 BIFF_XF_NUMFMT_USED = 0x01; +const sal_uInt8 BIFF_XF_FONT_USED = 0x02; +const sal_uInt8 BIFF_XF_ALIGN_USED = 0x04; +const sal_uInt8 BIFF_XF_BORDER_USED = 0x08; +const sal_uInt8 BIFF_XF_AREA_USED = 0x10; +const sal_uInt8 BIFF_XF_PROT_USED = 0x20; + +// BIFF XF text orientation +const sal_uInt8 BIFF_XF_ORIENT_NONE = 0; +const sal_uInt8 BIFF_XF_ORIENT_STACKED = 1; /// Stacked top to bottom. +const sal_uInt8 BIFF_XF_ORIENT_90CCW = 2; /// 90 degr. counterclockwise. +const sal_uInt8 BIFF_XF_ORIENT_90CW = 3; /// 90 degr. clockwise. + +// BIFF XF line styles +const sal_uInt8 BIFF_LINE_NONE = 0; +const sal_uInt8 BIFF_LINE_THIN = 1; + +// BIFF XF patterns +const sal_uInt8 BIFF_PATT_NONE = 0; +const sal_uInt8 BIFF_PATT_125 = 17; + +// BIFF2 XF flags +const sal_uInt8 BIFF2_XF_VALFMT_MASK = 0x3F; +const sal_uInt8 BIFF2_XF_LOCKED = 0x40; +const sal_uInt8 BIFF2_XF_HIDDEN = 0x80; +const sal_uInt8 BIFF2_XF_LEFTLINE = 0x08; +const sal_uInt8 BIFF2_XF_RIGHTLINE = 0x10; +const sal_uInt8 BIFF2_XF_TOPLINE = 0x20; +const sal_uInt8 BIFF2_XF_BOTTOMLINE = 0x40; +const sal_uInt8 BIFF2_XF_BACKGROUND = 0x80; + +// BIFF8 diagonal borders +const sal_uInt32 BIFF_XF_DIAG_TLBR = 0x40000000; /// Top-left to bottom-right. +const sal_uInt32 BIFF_XF_DIAG_BLTR = 0x80000000; /// Bottom-left to top-right. + +// BIFF STYLE flags +const sal_uInt16 BIFF_STYLE_BUILTIN = 0x8000; +const sal_uInt16 BIFF_STYLE_XFMASK = 0x0FFF; + +// BIFF conditional formatting +const sal_uInt32 BIFF_CFRULE_BORDER_LEFT = 0x00000400; +const sal_uInt32 BIFF_CFRULE_BORDER_RIGHT = 0x00000800; +const sal_uInt32 BIFF_CFRULE_BORDER_TOP = 0x00001000; +const sal_uInt32 BIFF_CFRULE_BORDER_BOTTOM = 0x00002000; +const sal_uInt32 BIFF_CFRULE_FILL_PATTERN = 0x00010000; +const sal_uInt32 BIFF_CFRULE_FILL_PATTCOLOR = 0x00020000; +const sal_uInt32 BIFF_CFRULE_FILL_FILLCOLOR = 0x00040000; +const sal_uInt32 BIFF_CFRULE_FONTBLOCK = 0x04000000; +const sal_uInt32 BIFF_CFRULE_ALIGNBLOCK = 0x08000000; +const sal_uInt32 BIFF_CFRULE_BORDERBLOCK = 0x10000000; +const sal_uInt32 BIFF_CFRULE_FILLBLOCK = 0x20000000; +const sal_uInt32 BIFF_CFRULE_PROTBLOCK = 0x40000000; + +const sal_uInt32 BIFF_CFRULE_FONT_STYLE = 0x00000002; /// Font posture or weight modified? +const sal_uInt32 BIFF_CFRULE_FONT_OUTLINE = 0x00000008; /// Font outline modified? +const sal_uInt32 BIFF_CFRULE_FONT_SHADOW = 0x00000010; /// Font shadow modified? +const sal_uInt32 BIFF_CFRULE_FONT_STRIKEOUT = 0x00000080; /// Font cancellation modified? +const sal_uInt32 BIFF_CFRULE_FONT_UNDERL = 0x00000001; /// Font underline type modified? +const sal_uInt32 BIFF_CFRULE_FONT_ESCAPEM = 0x00000001; /// Font escapement type modified? + +// ---------------------------------------------------------------------------- + +sal_Int32 lclGetRgbColor( sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB, sal_uInt8 nA ) +{ + sal_Int32 nValue = nA; + nValue <<= 8; + nValue |= nR; + nValue <<= 8; + nValue |= nG; + nValue <<= 8; + nValue |= nB; + return nValue; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +OoxColor::OoxColor() : + mfTint( 0.0 ), + mnType( XML_auto ), + mnValue( 0 ) +{ +} + +OoxColor::OoxColor( sal_Int32 nType, sal_Int32 nValue, double fTint ) : + mfTint( fTint ), + mnType( nType ), + mnValue( nValue ) +{ +} + +bool OoxColor::isAuto() const +{ + return mnType == XML_auto; +} + +void OoxColor::set( sal_Int32 nType, sal_Int32 nValue, double fTint ) +{ + mfTint = fTint; + mnType = nType; + mnValue = nValue; +} + +void OoxColor::importColor( const AttributeList& rAttribs ) +{ + mfTint = rAttribs.getDouble( XML_tint, 0.0 ); + if( rAttribs.getBool( XML_auto, false ) ) + { + mnType = XML_auto; + } + else if( rAttribs.hasAttribute( XML_rgb ) ) + { + mnType = XML_rgb; + mnValue = rAttribs.getHex( XML_rgb, API_RGB_TRANSPARENT ); + } + else if( rAttribs.hasAttribute( XML_theme ) ) + { + mnType = XML_theme; + mnValue = rAttribs.getInteger( XML_theme, -1 ); + } + else if( rAttribs.hasAttribute( XML_indexed ) ) + { + mnType = XML_indexed; + mnValue = rAttribs.getInteger( XML_indexed, -1 ); + } + else + { + mnType = XML_auto; + OSL_ENSURE( false, "OoxColor::importColor - unknown color type" ); + } +} + +void OoxColor::importColor( RecordInputStream& rStrm ) +{ + switch( rStrm.readuInt8() ) + { + case OOBIN_COLOR_AUTO: + mnType = XML_auto; + mfTint = 0.0; + rStrm.skip( 7 ); + break; + case OOBIN_COLOR_INDEXED: + mnType = XML_indexed; + mnValue = rStrm.readuInt8(); + mfTint = 0.0; + rStrm.skip( 6 ); + break; + case OOBIN_COLOR_RGB: + rStrm.skip( 3 ); + importColorRgb( rStrm ); + break; + case OOBIN_COLOR_THEME: + mnType = XML_theme; + mnValue = rStrm.readuInt8(); + // scale tint from signed 16-bit to double range -1.0 ... 1.0 + mfTint = static_cast< double >( rStrm.readInt16() ) / 0x7FFF; + rStrm.skip( 4 ); + break; + default: + OSL_ENSURE( false, "OoxColor::importColor - unknown color type" ); + mnType = XML_auto; + mfTint = 0.0; + rStrm.skip( 7 ); + } +} + +void OoxColor::importColorId( RecordInputStream& rStrm ) +{ + mfTint = 0.0; + mnType = XML_indexed; + rStrm >> mnValue; +} + +void OoxColor::importColorRgb( RecordInputStream& rStrm ) +{ + mfTint = 0.0; + mnType = XML_rgb; + sal_uInt8 nR, nG, nB, nA; + rStrm >> nR >> nG >> nB >> nA; + mnValue = lclGetRgbColor( nR, nG, nB, nA ); +} + +void OoxColor::importColorId( BiffInputStream& rStrm, bool b16Bit ) +{ + mfTint = 0.0; + mnType = XML_indexed; + mnValue = b16Bit ? rStrm.readuInt16() : rStrm.readuInt8(); +} + +void OoxColor::importColorRgb( BiffInputStream& rStrm ) +{ + mfTint = 0.0; + mnType = XML_rgb; + sal_uInt8 nR, nG, nB, nA; + rStrm >> nR >> nG >> nB >> nA; + mnValue = lclGetRgbColor( nR, nG, nB, nA ); +} + +RecordInputStream& operator>>( RecordInputStream& rStrm, OoxColor& orColor ) +{ + orColor.importColor( rStrm ); + return rStrm; +} + +// ============================================================================ + +namespace { + +/** Standard EGA colors, bright. */ +#define PALETTE_EGA_COLORS_LIGHT \ + 0x000000, 0xFFFFFF, 0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF +/** Standard EGA colors, dark. */ +#define PALETTE_EGA_COLORS_DARK \ + 0x800000, 0x008000, 0x000080, 0x808000, 0x800080, 0x008080, 0xC0C0C0, 0x808080 + +/** Default color table for BIFF2. */ +static const sal_Int32 spnDefColors2[] = +{ +/* 0 */ PALETTE_EGA_COLORS_LIGHT +}; + +/** Default color table for BIFF3/BIFF4. */ +static const sal_Int32 spnDefColors3[] = +{ +/* 0 */ PALETTE_EGA_COLORS_LIGHT, +/* 8 */ PALETTE_EGA_COLORS_LIGHT, +/* 16 */ PALETTE_EGA_COLORS_DARK +}; + +/** Default color table for BIFF5. */ +static const sal_Int32 spnDefColors5[] = +{ +/* 0 */ PALETTE_EGA_COLORS_LIGHT, +/* 8 */ PALETTE_EGA_COLORS_LIGHT, +/* 16 */ PALETTE_EGA_COLORS_DARK, +/* 24 */ 0x8080FF, 0x802060, 0xFFFFC0, 0xA0E0E0, 0x600080, 0xFF8080, 0x0080C0, 0xC0C0FF, +/* 32 */ 0x000080, 0xFF00FF, 0xFFFF00, 0x00FFFF, 0x800080, 0x800000, 0x008080, 0x0000FF, +/* 40 */ 0x00CFFF, 0x69FFFF, 0xE0FFE0, 0xFFFF80, 0xA6CAF0, 0xDD9CB3, 0xB38FEE, 0xE3E3E3, +/* 48 */ 0x2A6FF9, 0x3FB8CD, 0x488436, 0x958C41, 0x8E5E42, 0xA0627A, 0x624FAC, 0x969696, +/* 56 */ 0x1D2FBE, 0x286676, 0x004500, 0x453E01, 0x6A2813, 0x85396A, 0x4A3285, 0x424242 +}; + +/** Default color table for BIFF8/OOX. */ +static const sal_Int32 spnDefColors8[] = +{ +/* 0 */ PALETTE_EGA_COLORS_LIGHT, +/* 8 */ PALETTE_EGA_COLORS_LIGHT, +/* 16 */ PALETTE_EGA_COLORS_DARK, +/* 24 */ 0x9999FF, 0x993366, 0xFFFFCC, 0xCCFFFF, 0x660066, 0xFF8080, 0x0066CC, 0xCCCCFF, +/* 32 */ 0x000080, 0xFF00FF, 0xFFFF00, 0x00FFFF, 0x800080, 0x800000, 0x008080, 0x0000FF, +/* 40 */ 0x00CCFF, 0xCCFFFF, 0xCCFFCC, 0xFFFF99, 0x99CCFF, 0xFF99CC, 0xCC99FF, 0xFFCC99, +/* 48 */ 0x3366FF, 0x33CCCC, 0x99CC00, 0xFFCC00, 0xFF9900, 0xFF6600, 0x666699, 0x969696, +/* 56 */ 0x003366, 0x339966, 0x003300, 0x333300, 0x993300, 0x993366, 0x333399, 0x333333 +}; + +#undef PALETTE_EGA_COLORS_LIGHT +#undef PALETTE_EGA_COLORS_DARK + +} // namespace + +// ---------------------------------------------------------------------------- + +ColorPalette::ColorPalette( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnWindowColor( ThemeBuffer::getSystemWindowColor() ), + mnWinTextColor( ThemeBuffer::getSystemWindowTextColor() ) +{ + // default colors + switch( getFilterType() ) + { + case FILTER_OOX: + maColors.insert( maColors.begin(), spnDefColors8, STATIC_ARRAY_END( spnDefColors8 ) ); + mnAppendIndex = OOX_COLOR_USEROFFSET; + break; + case FILTER_BIFF: + switch( getBiff() ) + { + case BIFF2: maColors.insert( maColors.begin(), spnDefColors2, STATIC_ARRAY_END( spnDefColors2 ) ); break; + case BIFF3: + case BIFF4: maColors.insert( maColors.begin(), spnDefColors3, STATIC_ARRAY_END( spnDefColors3 ) ); break; + case BIFF5: maColors.insert( maColors.begin(), spnDefColors5, STATIC_ARRAY_END( spnDefColors5 ) ); break; + case BIFF8: maColors.insert( maColors.begin(), spnDefColors8, STATIC_ARRAY_END( spnDefColors8 ) ); break; + case BIFF_UNKNOWN: break; + } + mnAppendIndex = BIFF_COLOR_USEROFFSET; + break; + case FILTER_UNKNOWN: break; + } +} + +void ColorPalette::importPaletteColor( const AttributeList& rAttribs ) +{ + appendColor( rAttribs.getHex( XML_rgb, API_RGB_TRANSPARENT ) ); +} + +void ColorPalette::importPaletteColor( RecordInputStream& rStrm ) +{ + OoxColor aColor; + aColor.importColorRgb( rStrm ); + appendColor( aColor.mnValue ); +} + +void ColorPalette::importPalette( BiffInputStream& rStrm ) +{ + sal_uInt16 nCount; + rStrm >> nCount; + OSL_ENSURE( rStrm.getRecLeft() == static_cast< sal_uInt32 >( 4 * nCount ), + "ColorPalette::importPalette - wrong palette size" ); + + // fill palette from BIFF_COLOR_USEROFFSET + mnAppendIndex = BIFF_COLOR_USEROFFSET; + OoxColor aColor; + for( sal_uInt16 nIndex = 0; rStrm.isValid() && (nIndex < nCount); ++nIndex ) + { + aColor.importColorRgb( rStrm ); + appendColor( aColor.mnValue ); + } +} + +sal_Int32 ColorPalette::getColor( sal_Int32 nIndex ) const +{ + sal_Int32 nColor = API_RGB_TRANSPARENT; + if( (0 <= nIndex) && (static_cast< size_t >( nIndex ) < maColors.size()) ) + { + nColor = maColors[ nIndex ]; + } + else switch( nIndex ) + { + case OOX_COLOR_WINDOWTEXT3: + case OOX_COLOR_WINDOWTEXT: + case OOX_COLOR_CHWINDOWTEXT: nColor = mnWinTextColor; break; + case OOX_COLOR_WINDOWBACK3: + case OOX_COLOR_WINDOWBACK: + case OOX_COLOR_CHWINDOWBACK: nColor = mnWindowColor; break; +// case OOX_COLOR_BUTTONBACK: +// case OOX_COLOR_CHBORDERAUTO: +// case OOX_COLOR_NOTEBACK: +// case OOX_COLOR_NOTETEXT: + case OOX_COLOR_FONTAUTO: nColor = API_RGB_TRANSPARENT; break; + default: + OSL_ENSURE( false, "ColorPalette::getColor - unknown color index" ); + } + return nColor; +} + +void ColorPalette::appendColor( sal_Int32 nRGBValue ) +{ + if( mnAppendIndex < maColors.size() ) + maColors[ mnAppendIndex ] = nRGBValue; + else + maColors.push_back( nRGBValue ); + ++mnAppendIndex; +} + +// ============================================================================ + +OoxFontData::OoxFontData() : + mnScheme( XML_none ), + mnFamily( OOX_FONTFAMILY_NONE ), + mnCharSet( OOX_FONTCHARSET_ANSI ), + mfHeight( 0.0 ), + mnUnderline( XML_none ), + mnEscapement( XML_baseline ), + mbBold( false ), + mbItalic( false ), + mbStrikeout( false ), + mbOutline( false ), + mbShadow( false ) +{ +} + +void OoxFontData::setBinScheme( sal_uInt8 nScheme ) +{ + static const sal_Int32 spnSchemes[] = { XML_none, XML_major, XML_minor }; + mnScheme = STATIC_ARRAY_SELECT( spnSchemes, nScheme, XML_none ); +} + +void OoxFontData::setBiffHeight( sal_uInt16 nHeight ) +{ + mfHeight = nHeight / 20.0; // convert twips to points +} + +void OoxFontData::setBiffWeight( sal_uInt16 nWeight ) +{ + mbBold = nWeight >= BIFF_FONTWEIGHT_BOLD; +} + +void OoxFontData::setBiffUnderline( sal_uInt16 nUnderline ) +{ + switch( nUnderline ) + { + case BIFF_FONTUNDERL_NONE: mnUnderline = XML_none; break; + case BIFF_FONTUNDERL_SINGLE: mnUnderline = XML_single; break; + case BIFF_FONTUNDERL_DOUBLE: mnUnderline = XML_double; break; + case BIFF_FONTUNDERL_SINGLE_ACC: mnUnderline = XML_singleAccounting; break; + case BIFF_FONTUNDERL_DOUBLE_ACC: mnUnderline = XML_doubleAccounting; break; + default: mnUnderline = XML_none; + } +} + +void OoxFontData::setBiffEscapement( sal_uInt16 nEscapement ) +{ + static const sal_Int32 spnEscapes[] = { XML_baseline, XML_superscript, XML_subscript }; + mnEscapement = STATIC_ARRAY_SELECT( spnEscapes, nEscapement, XML_baseline ); +} + +// ============================================================================ + +Font::Font( const WorkbookHelper& rHelper, bool bDxf ) : + WorkbookHelper( rHelper ), + maOoxData( rHelper.getTheme().getDefaultFontData() ), + maUsedFlags( !bDxf ), + mbDxf( bDxf ) +{ +} + +Font::Font( const WorkbookHelper& rHelper, const OoxFontData& rFontData ) : + WorkbookHelper( rHelper ), + maOoxData( rFontData ), + maUsedFlags( true ), + mbDxf( false ) +{ +} + +bool Font::isSupportedContext( sal_Int32 nElement, sal_Int32 nParentContext ) +{ + switch( nParentContext ) + { + case XLS_TOKEN( font ): + return (nElement == XLS_TOKEN( name )) || + (nElement == XLS_TOKEN( scheme )) || + (nElement == XLS_TOKEN( charset )) || + (nElement == XLS_TOKEN( family )) || + (nElement == XLS_TOKEN( sz )) || + (nElement == XLS_TOKEN( color )) || + (nElement == XLS_TOKEN( u )) || + (nElement == XLS_TOKEN( vertAlign )) || + (nElement == XLS_TOKEN( b )) || + (nElement == XLS_TOKEN( i )) || + (nElement == XLS_TOKEN( outline )) || + (nElement == XLS_TOKEN( shadow )) || + (nElement == XLS_TOKEN( strike )); + + case XLS_TOKEN( rPr ): + return (nElement == XLS_TOKEN( rFont )) || + (nElement == XLS_TOKEN( scheme )) || + (nElement == XLS_TOKEN( charset )) || + (nElement == XLS_TOKEN( family )) || + (nElement == XLS_TOKEN( sz )) || + (nElement == XLS_TOKEN( color )) || + (nElement == XLS_TOKEN( u )) || + (nElement == XLS_TOKEN( vertAlign )) || + (nElement == XLS_TOKEN( b )) || + (nElement == XLS_TOKEN( i )) || + (nElement == XLS_TOKEN( outline )) || + (nElement == XLS_TOKEN( shadow )) || + (nElement == XLS_TOKEN( strike )) || + (nElement == XLS_TOKEN( vertAlign )); + } + return false; +} + +void Font::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + const OoxFontData& rDefFontData = getTheme().getDefaultFontData(); + switch( nElement ) + { + case XLS_TOKEN( name ): + case XLS_TOKEN( rFont ): + if( rAttribs.hasAttribute( XML_val ) ) + { + maOoxData.maName = rAttribs.getString( XML_val ); + maUsedFlags.mbNameUsed = true; + } + break; + case XLS_TOKEN( scheme ): + maOoxData.mnScheme = rAttribs.getToken( XML_val, rDefFontData.mnScheme ); + break; + case XLS_TOKEN( family ): + maOoxData.mnFamily = rAttribs.getInteger( XML_val, rDefFontData.mnFamily ); + break; + case XLS_TOKEN( charset ): + maOoxData.mnCharSet = rAttribs.getInteger( XML_val, rDefFontData.mnCharSet ); + break; + case XLS_TOKEN( sz ): + maOoxData.mfHeight = rAttribs.getDouble( XML_val, rDefFontData.mfHeight ); + maUsedFlags.mbHeightUsed = true; + break; + case XLS_TOKEN( color ): + maOoxData.maColor.importColor( rAttribs ); + maUsedFlags.mbColorUsed = true; + break; + case XLS_TOKEN( u ): + maOoxData.mnUnderline = rAttribs.getToken( XML_val, XML_single ); + maUsedFlags.mbUnderlineUsed = true; + break; + case XLS_TOKEN( vertAlign ): + maOoxData.mnEscapement = rAttribs.getToken( XML_val, XML_baseline ); + maUsedFlags.mbEscapementUsed = true; + break; + case XLS_TOKEN( b ): + maOoxData.mbBold = rAttribs.getBool( XML_val, true ); + maUsedFlags.mbWeightUsed = true; + break; + case XLS_TOKEN( i ): + maOoxData.mbItalic = rAttribs.getBool( XML_val, true ); + maUsedFlags.mbPostureUsed = true; + break; + case XLS_TOKEN( strike ): + maOoxData.mbStrikeout = rAttribs.getBool( XML_val, true ); + maUsedFlags.mbStrikeoutUsed = true; + break; + case XLS_TOKEN( outline ): + maOoxData.mbOutline = rAttribs.getBool( XML_val, true ); + maUsedFlags.mbOutlineUsed = true; + break; + case XLS_TOKEN( shadow ): + maOoxData.mbShadow = rAttribs.getBool( XML_val, true ); + maUsedFlags.mbShadowUsed = true; + break; + } +} + +void Font::importFont( RecordInputStream& rStrm ) +{ + OSL_ENSURE( !mbDxf, "Font::importFont - unexpected conditional formatting flag" ); + + sal_uInt16 nHeight, nFlags, nWeight, nEscapement; + sal_uInt8 nUnderline, nFamily, nCharSet, nScheme; + rStrm >> nHeight >> nFlags >> nWeight >> nEscapement >> nUnderline >> nFamily >> nCharSet; + rStrm.skip( 1 ); + rStrm >> maOoxData.maColor >> nScheme >> maOoxData.maName; + + // equal constants in BIFF and OOBIN for weight, underline, and escapement + maOoxData.setBinScheme( nScheme ); + maOoxData.setBiffHeight( nHeight ); + maOoxData.setBiffWeight( nWeight ); + maOoxData.setBiffUnderline( nUnderline ); + maOoxData.setBiffEscapement( nEscapement ); + maOoxData.mnFamily = nFamily; + maOoxData.mnCharSet = nCharSet; + // equal flags in BIFF and OOBIN + maOoxData.mbItalic = getFlag( nFlags, BIFF_FONTFLAG_ITALIC ); + maOoxData.mbStrikeout = getFlag( nFlags, BIFF_FONTFLAG_STRIKEOUT ); + maOoxData.mbOutline = getFlag( nFlags, BIFF_FONTFLAG_OUTLINE ); + maOoxData.mbShadow = getFlag( nFlags, BIFF_FONTFLAG_SHADOW ); +} + +void Font::importDxfName( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfName - missing conditional formatting flag" ); + maOoxData.maName = rStrm.readString( false ); + maUsedFlags.mbColorUsed = true; +} + +void Font::importDxfColor( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfColor - missing conditional formatting flag" ); + rStrm >> maOoxData.maColor; + maUsedFlags.mbColorUsed = true; +} + +void Font::importDxfScheme( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfScheme - missing conditional formatting flag" ); + maOoxData.setBinScheme( rStrm.readuInt8() ); + maUsedFlags.mbSchemeUsed = true; +} + +void Font::importDxfHeight( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfHeight - missing conditional formatting flag" ); + maOoxData.setBiffHeight( rStrm.readuInt16() ); + maUsedFlags.mbHeightUsed = true; +} + +void Font::importDxfWeight( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfWeight - missing conditional formatting flag" ); + maOoxData.setBiffWeight( rStrm.readuInt16() ); + maUsedFlags.mbWeightUsed = true; +} + +void Font::importDxfUnderline( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfUnderline - missing conditional formatting flag" ); + maOoxData.setBiffUnderline( rStrm.readuInt16() ); + maUsedFlags.mbUnderlineUsed = true; +} + +void Font::importDxfEscapement( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfEscapement - missing conditional formatting flag" ); + maOoxData.setBiffEscapement( rStrm.readuInt16() ); + maUsedFlags.mbEscapementUsed = true; +} + +void Font::importDxfFlag( sal_Int32 nElement, RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importDxfFlag - missing conditional formatting flag" ); + bool bFlag = rStrm.readuInt8() != 0; + switch( nElement ) + { + case XML_i: + maOoxData.mbItalic = bFlag; + maUsedFlags.mbPostureUsed = true; + break; + case XML_strike: + maOoxData.mbStrikeout = bFlag; + maUsedFlags.mbStrikeoutUsed = true; + break; + case XML_outline: + maOoxData.mbOutline = bFlag; + maUsedFlags.mbOutlineUsed = true; + break; + case XML_shadow: + maOoxData.mbShadow = bFlag; + maUsedFlags.mbShadowUsed = true; + break; + default: + OSL_ENSURE( false, "Font::importDxfFlag - unexpected element identifier" ); + } +} + +void Font::importFont( BiffInputStream& rStrm ) +{ + OSL_ENSURE( !mbDxf, "Font::importFont - unexpected conditional formatting flag" ); + switch( getBiff() ) + { + case BIFF2: + importFontData2( rStrm ); + importFontName2( rStrm ); + break; + case BIFF3: + case BIFF4: + importFontData2( rStrm ); + importFontColor( rStrm ); + importFontName2( rStrm ); + break; + case BIFF5: + importFontData2( rStrm ); + importFontColor( rStrm ); + importFontData5( rStrm ); + importFontName2( rStrm ); + break; + case BIFF8: + importFontData2( rStrm ); + importFontColor( rStrm ); + importFontData5( rStrm ); + importFontName8( rStrm ); + break; + case BIFF_UNKNOWN: break; + } +} + +void Font::importFontColor( BiffInputStream& rStrm ) +{ + OSL_ENSURE( !mbDxf, "Font::importFontColor - unexpected conditional formatting flag" ); + maOoxData.maColor.importColorId( rStrm ); +} + +void Font::importCfRule( BiffInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Font::importCfRule - missing conditional formatting flag" ); + + sal_Int32 nHeight, nColor; + sal_uInt32 nStyle, nFontFlags1, nFontFlags2, nFontFlags3; + sal_uInt16 nWeight, nEscapement; + sal_uInt8 nUnderline; + + OSL_ENSURE( rStrm.getRecLeft() >= 118, "Font::importCfRule - missing record data" ); + sal_uInt32 nRecPos = rStrm.getRecPos(); + maOoxData.maName = rStrm.readUniString( rStrm.readuInt8() ); + maUsedFlags.mbNameUsed = maOoxData.maName.getLength() > 0; + OSL_ENSURE( rStrm.isValid() && (rStrm.getRecPos() <= nRecPos + 64), "Font::importCfRule - font name too long" ); + rStrm.seek( nRecPos + 64 ); + rStrm >> nHeight >> nStyle >> nWeight >> nEscapement >> nUnderline; + rStrm.skip( 3 ); + rStrm >> nColor; + rStrm.skip( 4 ); + rStrm >> nFontFlags1 >> nFontFlags2 >> nFontFlags3; + rStrm.skip( 18 ); + + if( (maUsedFlags.mbColorUsed = (0 <= nColor) && (nColor <= 0x7FFF)) == true ) + maOoxData.maColor.set( XML_indexed, nColor ); + if( (maUsedFlags.mbHeightUsed = (0 < nHeight) && (nHeight <= 0x7FFF)) == true ) + maOoxData.setBiffHeight( static_cast< sal_uInt16 >( nHeight ) ); + if( (maUsedFlags.mbUnderlineUsed = !getFlag( nFontFlags3, BIFF_CFRULE_FONT_UNDERL )) == true ) + maOoxData.setBiffUnderline( nUnderline ); + if( (maUsedFlags.mbEscapementUsed = !getFlag( nFontFlags2, BIFF_CFRULE_FONT_ESCAPEM )) == true ) + maOoxData.setBiffEscapement( nEscapement ); + if( (maUsedFlags.mbWeightUsed = maUsedFlags.mbPostureUsed = !getFlag( nFontFlags1, BIFF_CFRULE_FONT_STYLE )) == true ) + { + maOoxData.setBiffWeight( nWeight ); + maOoxData.mbItalic = getFlag( nStyle, BIFF_CFRULE_FONT_STYLE ); + } + if( (maUsedFlags.mbStrikeoutUsed = !getFlag( nFontFlags1, BIFF_CFRULE_FONT_STRIKEOUT )) == true ) + maOoxData.mbStrikeout = getFlag( nStyle, BIFF_CFRULE_FONT_STRIKEOUT ); + if( (maUsedFlags.mbOutlineUsed = !getFlag( nFontFlags1, BIFF_CFRULE_FONT_OUTLINE )) == true ) + maOoxData.mbOutline = getFlag( nStyle, BIFF_CFRULE_FONT_OUTLINE ); + if( (maUsedFlags.mbShadowUsed = !getFlag( nFontFlags1, BIFF_CFRULE_FONT_SHADOW )) == true ) + maOoxData.mbShadow = getFlag( nStyle, BIFF_CFRULE_FONT_SHADOW ); +} + +rtl_TextEncoding Font::getFontEncoding() const +{ + // #i63105# cells use text encoding from FONT record character set + // #i67768# BIFF2-BIFF4 FONT records do not contain character set + // #i71033# do not use maApiData, this function is used before finalizeImport() + rtl_TextEncoding eFontEnc = RTL_TEXTENCODING_DONTKNOW; + if( (0 <= maOoxData.mnCharSet) && (maOoxData.mnCharSet <= SAL_MAX_UINT8) ) + eFontEnc = rtl_getTextEncodingFromWindowsCharset( static_cast< sal_uInt8 >( maOoxData.mnCharSet ) ); + return (eFontEnc == RTL_TEXTENCODING_DONTKNOW) ? getTextEncoding() : eFontEnc; +} + +void Font::finalizeImport() +{ + namespace cssawt = ::com::sun::star::awt; + + // font name + maApiData.maDesc.Name = maOoxData.maName; + + // font family + switch( maOoxData.mnFamily ) + { + case OOX_FONTFAMILY_NONE: maApiData.maDesc.Family = cssawt::FontFamily::DONTKNOW; break; + case OOX_FONTFAMILY_ROMAN: maApiData.maDesc.Family = cssawt::FontFamily::ROMAN; break; + case OOX_FONTFAMILY_SWISS: maApiData.maDesc.Family = cssawt::FontFamily::SWISS; break; + case OOX_FONTFAMILY_MODERN: maApiData.maDesc.Family = cssawt::FontFamily::MODERN; break; + case OOX_FONTFAMILY_SCRIPT: maApiData.maDesc.Family = cssawt::FontFamily::SCRIPT; break; + case OOX_FONTFAMILY_DECORATIVE: maApiData.maDesc.Family = cssawt::FontFamily::DECORATIVE; break; + } + + // character set + if( (0 <= maOoxData.mnCharSet) && (maOoxData.mnCharSet <= 255) ) + maApiData.maDesc.CharSet = static_cast< sal_Int16 >( + rtl_getTextEncodingFromWindowsCharset( static_cast< sal_uInt8 >( maOoxData.mnCharSet ) ) ); + + // color, height, weight, slant, strikeout, outline, shadow + maApiData.mnColor = getStyles().getColor( maOoxData.maColor, API_RGB_TRANSPARENT ); + maApiData.maDesc.Height = static_cast< sal_Int16 >( maOoxData.mfHeight * 20.0 ); + maApiData.maDesc.Weight = maOoxData.mbBold ? cssawt::FontWeight::BOLD : cssawt::FontWeight::NORMAL; + maApiData.maDesc.Slant = maOoxData.mbItalic ? cssawt::FontSlant_ITALIC : cssawt::FontSlant_NONE; + maApiData.maDesc.Strikeout = maOoxData.mbStrikeout ? cssawt::FontStrikeout::SINGLE : cssawt::FontStrikeout::NONE; + maApiData.mbOutline = maOoxData.mbOutline; + maApiData.mbShadow = maOoxData.mbShadow; + + // underline + switch( maOoxData.mnUnderline ) + { + case XML_double: maApiData.maDesc.Underline = cssawt::FontUnderline::DOUBLE; break; + case XML_doubleAccounting: maApiData.maDesc.Underline = cssawt::FontUnderline::DOUBLE; break; + case XML_none: maApiData.maDesc.Underline = cssawt::FontUnderline::NONE; break; + case XML_single: maApiData.maDesc.Underline = cssawt::FontUnderline::SINGLE; break; + case XML_singleAccounting: maApiData.maDesc.Underline = cssawt::FontUnderline::SINGLE; break; + } + + // escapement + switch( maOoxData.mnEscapement ) + { + case XML_baseline: + maApiData.mnEscapement = API_ESCAPE_NONE; + maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_NONE; + break; + case XML_superscript: + maApiData.mnEscapement = API_ESCAPE_SUPERSCRIPT; + maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_DEFAULT; + break; + case XML_subscript: + maApiData.mnEscapement = API_ESCAPE_SUBSCRIPT; + maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_DEFAULT; + break; + } + + // supported script types + if( maUsedFlags.mbNameUsed ) + { + Reference< XDevice > xDevice = getReferenceDevice(); + if( xDevice.is() ) + { + Reference< XFont2 > xFont( xDevice->getFont( maApiData.maDesc ), UNO_QUERY ); + if( xFont.is() ) + { + // #91658# CJK fonts + maApiData.mbHasAsian = + xFont->hasGlyphs( OUString( sal_Unicode( 0x3041 ) ) ) || // 3040-309F: Hiragana + xFont->hasGlyphs( OUString( sal_Unicode( 0x30A1 ) ) ) || // 30A0-30FF: Katakana + xFont->hasGlyphs( OUString( sal_Unicode( 0x3111 ) ) ) || // 3100-312F: Bopomofo + xFont->hasGlyphs( OUString( sal_Unicode( 0x3131 ) ) ) || // 3130-318F: Hangul Compatibility Jamo + xFont->hasGlyphs( OUString( sal_Unicode( 0x3301 ) ) ) || // 3300-33FF: CJK Compatibility + xFont->hasGlyphs( OUString( sal_Unicode( 0x3401 ) ) ) || // 3400-4DBF: CJK Unified Ideographs Extension A + xFont->hasGlyphs( OUString( sal_Unicode( 0x4E01 ) ) ) || // 4E00-9FAF: CJK Unified Ideographs + xFont->hasGlyphs( OUString( sal_Unicode( 0x7E01 ) ) ) || // 4E00-9FAF: CJK unified ideographs + xFont->hasGlyphs( OUString( sal_Unicode( 0xA001 ) ) ) || // A001-A48F: Yi Syllables + xFont->hasGlyphs( OUString( sal_Unicode( 0xAC01 ) ) ) || // AC00-D7AF: Hangul Syllables + xFont->hasGlyphs( OUString( sal_Unicode( 0xCC01 ) ) ) || // AC00-D7AF: Hangul Syllables + xFont->hasGlyphs( OUString( sal_Unicode( 0xF901 ) ) ) || // F900-FAFF: CJK Compatibility Ideographs + xFont->hasGlyphs( OUString( sal_Unicode( 0xFF71 ) ) ); // FF00-FFEF: Halfwidth/Fullwidth Forms + // #113783# CTL fonts + maApiData.mbHasCmplx = + xFont->hasGlyphs( OUString( sal_Unicode( 0x05D1 ) ) ) || // 0590-05FF: Hebrew + xFont->hasGlyphs( OUString( sal_Unicode( 0x0631 ) ) ) || // 0600-06FF: Arabic + xFont->hasGlyphs( OUString( sal_Unicode( 0x0721 ) ) ) || // 0700-074F: Syriac + xFont->hasGlyphs( OUString( sal_Unicode( 0x0911 ) ) ) || // 0900-0DFF: Indic scripts + xFont->hasGlyphs( OUString( sal_Unicode( 0x0E01 ) ) ) || // 0E00-0E7F: Thai + xFont->hasGlyphs( OUString( sal_Unicode( 0xFB21 ) ) ) || // FB1D-FB4F: Hebrew Presentation Forms + xFont->hasGlyphs( OUString( sal_Unicode( 0xFB51 ) ) ) || // FB50-FDFF: Arabic Presentation Forms-A + xFont->hasGlyphs( OUString( sal_Unicode( 0xFE71 ) ) ); // FE70-FEFF: Arabic Presentation Forms-B + // Western fonts + maApiData.mbHasWstrn = + (!maApiData.mbHasAsian && !maApiData.mbHasCmplx) || + xFont->hasGlyphs( OUString( sal_Unicode( 'A' ) ) ); + } + } + } +} + +const FontDescriptor& Font::getFontDescriptor() const +{ + return maApiData.maDesc; +} + +bool Font::needsRichTextFormat() const +{ + return maApiData.mnEscapement != API_ESCAPE_NONE; +} + +void Font::writeToPropertySet( PropertySet& rPropSet, FontPropertyType ePropType ) const +{ + getStylesPropertyHelper().writeFontProperties( rPropSet, maApiData, maUsedFlags, ePropType ); +} + +void Font::importFontData2( BiffInputStream& rStrm ) +{ + sal_uInt16 nHeight, nFlags; + rStrm >> nHeight >> nFlags; + + maOoxData.setBiffHeight( nHeight ); + maOoxData.mnFamily = OOX_FONTFAMILY_NONE; + maOoxData.mnCharSet = OOX_FONTCHARSET_UNUSED; // ensure to not use font charset in byte string import + maOoxData.mnUnderline = getFlagValue( nFlags, BIFF_FONTFLAG_UNDERLINE, XML_single, XML_none ); + maOoxData.mnEscapement = XML_none; + maOoxData.mbBold = getFlag( nFlags, BIFF_FONTFLAG_BOLD ); + maOoxData.mbItalic = getFlag( nFlags, BIFF_FONTFLAG_ITALIC ); + maOoxData.mbStrikeout = getFlag( nFlags, BIFF_FONTFLAG_STRIKEOUT ); + maOoxData.mbOutline = getFlag( nFlags, BIFF_FONTFLAG_OUTLINE ); + maOoxData.mbShadow = getFlag( nFlags, BIFF_FONTFLAG_SHADOW ); +} + +void Font::importFontData5( BiffInputStream& rStrm ) +{ + sal_uInt16 nWeight, nEscapement; + sal_uInt8 nUnderline, nFamily, nCharSet; + rStrm >> nWeight >> nEscapement >> nUnderline >> nFamily >> nCharSet; + rStrm.skip( 1 ); + + maOoxData.setBiffWeight( nWeight ); + maOoxData.setBiffUnderline( nUnderline ); + maOoxData.setBiffEscapement( nEscapement ); + // equal constants in XML and BIFF for family and charset + maOoxData.mnFamily = nFamily; + maOoxData.mnCharSet = nCharSet; +} + +void Font::importFontName2( BiffInputStream& rStrm ) +{ + maOoxData.maName = rStrm.readByteString( false, getTextEncoding() ); +} + +void Font::importFontName8( BiffInputStream& rStrm ) +{ + maOoxData.maName = rStrm.readUniString( rStrm.readuInt8() ); +} + +// ============================================================================ + +OoxAlignmentData::OoxAlignmentData() : + mnHorAlign( XML_general ), + mnVerAlign( XML_bottom ), + mnTextDir( OOX_XF_TEXTDIR_CONTEXT ), + mnRotation( OOX_XF_ROTATION_NONE ), + mnIndent( OOX_XF_INDENT_NONE ), + mbWrapText( false ), + mbShrink( false ), + mbJustLastLine( false ) +{ +} + +void OoxAlignmentData::setBinHorAlign( sal_uInt8 nHorAlign ) +{ + static const sal_Int32 spnHorAligns[] = { + XML_general, XML_left, XML_center, XML_right, + XML_fill, XML_justify, XML_centerContinuous, XML_distributed }; + mnHorAlign = STATIC_ARRAY_SELECT( spnHorAligns, nHorAlign, XML_general ); +} + +void OoxAlignmentData::setBinVerAlign( sal_uInt8 nVerAlign ) +{ + static const sal_Int32 spnVerAligns[] = { + XML_top, XML_center, XML_bottom, XML_justify, XML_distributed }; + mnVerAlign = STATIC_ARRAY_SELECT( spnVerAligns, nVerAlign, XML_bottom ); +} + +void OoxAlignmentData::setBinTextOrient( sal_uInt8 nTextOrient ) +{ + static const sal_Int32 spnRotations[] = { + OOX_XF_ROTATION_NONE, OOX_XF_ROTATION_STACKED, + OOX_XF_ROTATION_90CCW, OOX_XF_ROTATION_90CW }; + mnRotation = STATIC_ARRAY_SELECT( spnRotations, nTextOrient, OOX_XF_ROTATION_NONE ); +} + +// ============================================================================ + +Alignment::Alignment( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void Alignment::importAlignment( const AttributeList& rAttribs ) +{ + maOoxData.mnHorAlign = rAttribs.getToken( XML_horizontal, XML_general ); + maOoxData.mnVerAlign = rAttribs.getToken( XML_vertical, XML_bottom ); + maOoxData.mnTextDir = rAttribs.getInteger( XML_readingOrder, OOX_XF_TEXTDIR_CONTEXT ); + maOoxData.mnRotation = rAttribs.getInteger( XML_textRotation, OOX_XF_ROTATION_NONE ); + maOoxData.mnIndent = rAttribs.getInteger( XML_indent, OOX_XF_INDENT_NONE ); + maOoxData.mbWrapText = rAttribs.getBool( XML_wrapText, false ); + maOoxData.mbShrink = rAttribs.getBool( XML_shrinkToFit, false ); + maOoxData.mbJustLastLine = rAttribs.getBool( XML_justifyLastLine, false ); +} + +void Alignment::setBinData( sal_uInt32 nFlags ) +{ + maOoxData.setBinHorAlign( extractValue< sal_uInt8 >( nFlags, 16, 3 ) ); + maOoxData.setBinVerAlign( extractValue< sal_uInt8 >( nFlags, 19, 3 ) ); + maOoxData.mnTextDir = extractValue< sal_Int32 >( nFlags, 26, 2 ); + maOoxData.mnRotation = extractValue< sal_Int32 >( nFlags, 0, 8 ); + maOoxData.mnIndent = extractValue< sal_uInt8 >( nFlags, 8, 8 ); + maOoxData.mbWrapText = getFlag( nFlags, OOBIN_XF_WRAPTEXT ); + maOoxData.mbShrink = getFlag( nFlags, OOBIN_XF_SHRINK ); + maOoxData.mbJustLastLine = getFlag( nFlags, OOBIN_XF_JUSTLASTLINE ); +} + +void Alignment::setBiff2Data( sal_uInt8 nFlags ) +{ + maOoxData.setBinHorAlign( extractValue< sal_uInt8 >( nFlags, 0, 3 ) ); +} + +void Alignment::setBiff3Data( sal_uInt16 nAlign ) +{ + maOoxData.setBinHorAlign( extractValue< sal_uInt8 >( nAlign, 0, 3 ) ); + maOoxData.mbWrapText = getFlag( nAlign, BIFF_XF_WRAPTEXT ); // new in BIFF3 +} + +void Alignment::setBiff4Data( sal_uInt16 nAlign ) +{ + maOoxData.setBinHorAlign( extractValue< sal_uInt8 >( nAlign, 0, 3 ) ); + maOoxData.setBinVerAlign( extractValue< sal_uInt8 >( nAlign, 4, 2 ) ); // new in BIFF4 + maOoxData.setBinTextOrient( extractValue< sal_uInt8 >( nAlign, 6, 2 ) ); // new in BIFF4 + maOoxData.mbWrapText = getFlag( nAlign, BIFF_XF_WRAPTEXT ); +} + +void Alignment::setBiff5Data( sal_uInt16 nAlign ) +{ + maOoxData.setBinHorAlign( extractValue< sal_uInt8 >( nAlign, 0, 3 ) ); + maOoxData.setBinVerAlign( extractValue< sal_uInt8 >( nAlign, 4, 3 ) ); + maOoxData.setBinTextOrient( extractValue< sal_uInt8 >( nAlign, 8, 2 ) ); + maOoxData.mbWrapText = getFlag( nAlign, BIFF_XF_WRAPTEXT ); +} + +void Alignment::setBiff8Data( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib ) +{ + maOoxData.setBinHorAlign( extractValue< sal_uInt8 >( nAlign, 0, 3 ) ); + maOoxData.setBinVerAlign( extractValue< sal_uInt8 >( nAlign, 4, 3 ) ); + maOoxData.mnTextDir = extractValue< sal_Int32 >( nMiscAttrib, 6, 2 ); // new in BIFF8 + maOoxData.mnRotation = extractValue< sal_Int32 >( nAlign, 8, 8 ); // new in BIFF8 + maOoxData.mnIndent = extractValue< sal_uInt8 >( nMiscAttrib, 0, 4 ); // new in BIFF8 + maOoxData.mbWrapText = getFlag( nAlign, BIFF_XF_WRAPTEXT ); + maOoxData.mbShrink = getFlag( nMiscAttrib, BIFF_XF_SHRINK ); // new in BIFF8 + maOoxData.mbJustLastLine = getFlag( nAlign, BIFF_XF_JUSTLASTLINE ); // new in BIFF8(?) +} + +void Alignment::finalizeImport() +{ + namespace csstab = ::com::sun::star::table; + namespace csstxt = ::com::sun::star::text; + + // horizontal alignment + switch( maOoxData.mnHorAlign ) + { + case XML_center: maApiData.meHorJustify = csstab::CellHoriJustify_CENTER; break; + case XML_centerContinuous: maApiData.meHorJustify = csstab::CellHoriJustify_CENTER; break; + case XML_distributed: maApiData.meHorJustify = csstab::CellHoriJustify_BLOCK; break; + case XML_fill: maApiData.meHorJustify = csstab::CellHoriJustify_REPEAT; break; + case XML_general: maApiData.meHorJustify = csstab::CellHoriJustify_STANDARD; break; + case XML_justify: maApiData.meHorJustify = csstab::CellHoriJustify_BLOCK; break; + case XML_left: maApiData.meHorJustify = csstab::CellHoriJustify_LEFT; break; + case XML_right: maApiData.meHorJustify = csstab::CellHoriJustify_RIGHT; break; + } + + // vertical alignment + switch( maOoxData.mnVerAlign ) + { + case XML_bottom: maApiData.meVerJustify = csstab::CellVertJustify_BOTTOM; break; + case XML_center: maApiData.meVerJustify = csstab::CellVertJustify_CENTER; break; + case XML_distributed: maApiData.meVerJustify = csstab::CellVertJustify_TOP; break; + case XML_justify: maApiData.meVerJustify = csstab::CellVertJustify_TOP; break; + case XML_top: maApiData.meVerJustify = csstab::CellVertJustify_TOP; break; + } + + /* indentation: expressed as number of blocks of 3 space characters in + OOX, and as multiple of 10 points in BIFF. */ + sal_Int32 nIndent = 0; + switch( getFilterType() ) + { + case FILTER_OOX: nIndent = getUnitConverter().calcMm100FromSpaces( 3.0 * maOoxData.mnIndent ); break; + case FILTER_BIFF: nIndent = getUnitConverter().calcMm100FromPoints( 10.0 * maOoxData.mnIndent ); break; + case FILTER_UNKNOWN: break; + } + if( (0 <= nIndent) && (nIndent <= SAL_MAX_INT16) ) + maApiData.mnIndent = static_cast< sal_Int16 >( nIndent ); + + // complex text direction + switch( maOoxData.mnTextDir ) + { + case OOX_XF_TEXTDIR_CONTEXT: maApiData.mnWritingMode = csstxt::WritingMode2::PAGE; break; + case OOX_XF_TEXTDIR_LTR: maApiData.mnWritingMode = csstxt::WritingMode2::LR_TB; break; + case OOX_XF_TEXTDIR_RTL: maApiData.mnWritingMode = csstxt::WritingMode2::RL_TB; break; + } + + // rotation: 0-90 means 0 to 90 degrees ccw, 91-180 means 1 to 90 degrees cw, 255 means stacked + sal_Int32 nOoxRot = maOoxData.mnRotation; + maApiData.mnRotation = ((0 <= nOoxRot) && (nOoxRot <= 90)) ? + (100 * nOoxRot) : + (((91 <= nOoxRot) && (nOoxRot <= 180)) ? (100 * (450 - nOoxRot)) : 0); + + // "Orientation" property used for character stacking + maApiData.meOrientation = (nOoxRot == OOX_XF_ROTATION_STACKED) ? + csstab::CellOrientation_STACKED : csstab::CellOrientation_STANDARD; + + // alignment flags + maApiData.mbWrapText = maOoxData.mbWrapText; + maApiData.mbShrink = maOoxData.mbShrink; + +} + +void Alignment::writeToPropertySet( PropertySet& rPropSet ) const +{ + getStylesPropertyHelper().writeAlignmentProperties( rPropSet, maApiData ); +} + +// ============================================================================ + +OoxProtectionData::OoxProtectionData() : + mbLocked( true ), // default in Excel and Calc + mbHidden( false ) +{ +} + +// ============================================================================ + +Protection::Protection( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void Protection::importProtection( const AttributeList& rAttribs ) +{ + maOoxData.mbLocked = rAttribs.getBool( XML_locked, true ); + maOoxData.mbHidden = rAttribs.getBool( XML_hidden, false ); +} + +void Protection::setBinData( sal_uInt32 nFlags ) +{ + maOoxData.mbLocked = getFlag( nFlags, OOBIN_XF_LOCKED ); + maOoxData.mbHidden = getFlag( nFlags, OOBIN_XF_HIDDEN ); +} + +void Protection::setBiff2Data( sal_uInt8 nNumFmt ) +{ + maOoxData.mbLocked = getFlag( nNumFmt, BIFF2_XF_LOCKED ); + maOoxData.mbHidden = getFlag( nNumFmt, BIFF2_XF_HIDDEN ); +} + +void Protection::setBiff3Data( sal_uInt16 nProt ) +{ + maOoxData.mbLocked = getFlag( nProt, BIFF_XF_LOCKED ); + maOoxData.mbHidden = getFlag( nProt, BIFF_XF_HIDDEN ); +} + +void Protection::finalizeImport() +{ + maApiData.maCellProt.IsLocked = maOoxData.mbLocked; + maApiData.maCellProt.IsFormulaHidden = maOoxData.mbHidden; +} + +void Protection::writeToPropertySet( PropertySet& rPropSet ) const +{ + getStylesPropertyHelper().writeProtectionProperties( rPropSet, maApiData ); +} + +// ============================================================================ + +OoxBorderLineData::OoxBorderLineData( bool bDxf ) : + maColor( XML_indexed, OOX_COLOR_WINDOWTEXT ), + mnStyle( XML_none ), + mbUsed( !bDxf ) +{ +} + +void OoxBorderLineData::setBiffStyle( sal_Int32 nLineStyle ) +{ + static const sal_Int32 spnStyleIds[] = { + XML_none, XML_thin, XML_medium, XML_dashed, + XML_dotted, XML_thick, XML_double, XML_hair, + XML_mediumDashed, XML_dashDot, XML_mediumDashDot, XML_dashDotDot, + XML_mediumDashDotDot, XML_slantDashDot }; + mnStyle = STATIC_ARRAY_SELECT( spnStyleIds, nLineStyle, XML_none ); +} + +void OoxBorderLineData::setBiffData( sal_uInt8 nLineStyle, sal_uInt16 nLineColor ) +{ + maColor.set( XML_indexed, nLineColor ); + setBiffStyle( nLineStyle ); +} + +// ============================================================================ + +OoxBorderData::OoxBorderData( bool bDxf ) : + maLeft( bDxf ), + maRight( bDxf ), + maTop( bDxf ), + maBottom( bDxf ), + maDiagonal( bDxf ), + mbDiagTLtoBR( false ), + mbDiagBLtoTR( false ) +{ +} + +// ============================================================================ + +namespace { + +inline void lclSetBorderLineWidth( BorderLine& rBorderLine, + sal_Int16 nOuter, sal_Int16 nDist = API_LINE_NONE, sal_Int16 nInner = API_LINE_NONE ) +{ + rBorderLine.OuterLineWidth = nOuter; + rBorderLine.LineDistance = nDist; + rBorderLine.InnerLineWidth = nInner; +} + +inline sal_Int32 lclGetBorderLineWidth( const BorderLine& rBorderLine ) +{ + return rBorderLine.OuterLineWidth + rBorderLine.LineDistance + rBorderLine.InnerLineWidth; +} + +const BorderLine* lclGetThickerLine( const BorderLine& rBorderLine1, sal_Bool bValid1, const BorderLine& rBorderLine2, sal_Bool bValid2 ) +{ + if( bValid1 && bValid2 ) + return (lclGetBorderLineWidth( rBorderLine1 ) < lclGetBorderLineWidth( rBorderLine2 )) ? &rBorderLine2 : &rBorderLine1; + if( bValid1 ) + return &rBorderLine1; + if( bValid2 ) + return &rBorderLine2; + return 0; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +Border::Border( const WorkbookHelper& rHelper, bool bDxf ) : + WorkbookHelper( rHelper ), + maOoxData( bDxf ), + mbDxf( bDxf ) +{ +} + +bool Border::isSupportedContext( sal_Int32 nElement, sal_Int32 nParentContext ) +{ + switch( nParentContext ) + { + case XLS_TOKEN( border ): + return (nElement == XLS_TOKEN( left )) || + (nElement == XLS_TOKEN( right )) || + (nElement == XLS_TOKEN( top )) || + (nElement == XLS_TOKEN( bottom )) || + (nElement == XLS_TOKEN( diagonal )); + case XLS_TOKEN( left ): + case XLS_TOKEN( right ): + case XLS_TOKEN( top ): + case XLS_TOKEN( bottom ): + case XLS_TOKEN( diagonal ): + return (nElement == XLS_TOKEN( color )); + } + return false; +} + +void Border::importBorder( const AttributeList& rAttribs ) +{ + maOoxData.mbDiagTLtoBR = rAttribs.getBool( XML_diagonalDown, false ); + maOoxData.mbDiagBLtoTR = rAttribs.getBool( XML_diagonalUp, false ); +} + +void Border::importStyle( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + if( OoxBorderLineData* pBorderLine = getBorderLine( nElement ) ) + { + pBorderLine->mnStyle = rAttribs.getToken( XML_style, XML_none ); + pBorderLine->mbUsed = true; + } +} + +void Border::importColor( sal_Int32 nElement, const AttributeList& rAttribs ) +{ + if( OoxBorderLineData* pBorderLine = getBorderLine( nElement ) ) + pBorderLine->maColor.importColor( rAttribs ); +} + +void Border::importBorder( RecordInputStream& rStrm ) +{ + sal_uInt8 nFlags = rStrm.readuInt8(); + maOoxData.mbDiagTLtoBR = getFlag( nFlags, OOBIN_BORDER_DIAG_TLBR ); + maOoxData.mbDiagBLtoTR = getFlag( nFlags, OOBIN_BORDER_DIAG_BLTR ); + maOoxData.maTop.setBiffStyle( rStrm.readuInt16() ); + rStrm >> maOoxData.maTop.maColor; + maOoxData.maBottom.setBiffStyle( rStrm.readuInt16() ); + rStrm >> maOoxData.maBottom.maColor; + maOoxData.maLeft.setBiffStyle( rStrm.readuInt16() ); + rStrm >> maOoxData.maLeft.maColor; + maOoxData.maRight.setBiffStyle( rStrm.readuInt16() ); + rStrm >> maOoxData.maRight.maColor; + maOoxData.maDiagonal.setBiffStyle( rStrm.readuInt16() ); + rStrm >> maOoxData.maDiagonal.maColor; +} + +void Border::importDxfBorder( sal_Int32 nElement, RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Border::importDxfBorder - missing conditional formatting flag" ); + if( OoxBorderLineData* pBorderLine = getBorderLine( nElement ) ) + { + sal_uInt16 nStyle; + rStrm >> pBorderLine->maColor >> nStyle; + pBorderLine->setBiffStyle( nStyle ); + pBorderLine->mbUsed = true; + } +} + +void Border::setBiff2Data( sal_uInt8 nFlags ) +{ + OSL_ENSURE( !mbDxf, "Border::setBiff2Data - unexpected conditional formatting flag" ); + maOoxData.maLeft.setBiffData( getFlagValue( nFlags, BIFF2_XF_LEFTLINE, BIFF_LINE_THIN, BIFF_LINE_NONE ), BIFF2_COLOR_BLACK ); + maOoxData.maRight.setBiffData( getFlagValue( nFlags, BIFF2_XF_RIGHTLINE, BIFF_LINE_THIN, BIFF_LINE_NONE ), BIFF2_COLOR_BLACK ); + maOoxData.maTop.setBiffData( getFlagValue( nFlags, BIFF2_XF_TOPLINE, BIFF_LINE_THIN, BIFF_LINE_NONE ), BIFF2_COLOR_BLACK ); + maOoxData.maBottom.setBiffData( getFlagValue( nFlags, BIFF2_XF_BOTTOMLINE, BIFF_LINE_THIN, BIFF_LINE_NONE ), BIFF2_COLOR_BLACK ); + maOoxData.maDiagonal.mbUsed = false; +} + +void Border::setBiff3Data( sal_uInt32 nBorder ) +{ + OSL_ENSURE( !mbDxf, "Border::setBiff3Data - unexpected conditional formatting flag" ); + maOoxData.maLeft.setBiffData( extractValue< sal_uInt8 >( nBorder, 8, 3 ), extractValue< sal_uInt16 >( nBorder, 11, 5 ) ); + maOoxData.maRight.setBiffData( extractValue< sal_uInt8 >( nBorder, 24, 3 ), extractValue< sal_uInt16 >( nBorder, 27, 5 ) ); + maOoxData.maTop.setBiffData( extractValue< sal_uInt8 >( nBorder, 0, 3 ), extractValue< sal_uInt16 >( nBorder, 3, 5 ) ); + maOoxData.maBottom.setBiffData( extractValue< sal_uInt8 >( nBorder, 16, 3 ), extractValue< sal_uInt16 >( nBorder, 19, 5 ) ); + maOoxData.maDiagonal.mbUsed = false; +} + +void Border::setBiff5Data( sal_uInt32 nBorder, sal_uInt32 nArea ) +{ + OSL_ENSURE( !mbDxf, "Border::setBiff5Data - unexpected conditional formatting flag" ); + maOoxData.maLeft.setBiffData( extractValue< sal_uInt8 >( nBorder, 3, 3 ), extractValue< sal_uInt16 >( nBorder, 16, 7 ) ); + maOoxData.maRight.setBiffData( extractValue< sal_uInt8 >( nBorder, 6, 3 ), extractValue< sal_uInt16 >( nBorder, 23, 7 ) ); + maOoxData.maTop.setBiffData( extractValue< sal_uInt8 >( nBorder, 0, 3 ), extractValue< sal_uInt16 >( nBorder, 9, 7 ) ); + maOoxData.maBottom.setBiffData( extractValue< sal_uInt8 >( nArea, 22, 3 ), extractValue< sal_uInt16 >( nArea, 25, 7 ) ); + maOoxData.maDiagonal.mbUsed = false; +} + +void Border::setBiff8Data( sal_uInt32 nBorder1, sal_uInt32 nBorder2 ) +{ + OSL_ENSURE( !mbDxf, "Border::setBiff8Data - unexpected conditional formatting flag" ); + maOoxData.maLeft.setBiffData( extractValue< sal_uInt8 >( nBorder1, 0, 4 ), extractValue< sal_uInt16 >( nBorder1, 16, 7 ) ); + maOoxData.maRight.setBiffData( extractValue< sal_uInt8 >( nBorder1, 4, 4 ), extractValue< sal_uInt16 >( nBorder1, 23, 7 ) ); + maOoxData.maTop.setBiffData( extractValue< sal_uInt8 >( nBorder1, 8, 4 ), extractValue< sal_uInt16 >( nBorder2, 0, 7 ) ); + maOoxData.maBottom.setBiffData( extractValue< sal_uInt8 >( nBorder1, 12, 4 ), extractValue< sal_uInt16 >( nBorder2, 7, 7 ) ); + maOoxData.mbDiagTLtoBR = getFlag( nBorder1, BIFF_XF_DIAG_TLBR ); + maOoxData.mbDiagBLtoTR = getFlag( nBorder1, BIFF_XF_DIAG_BLTR ); + if( maOoxData.mbDiagTLtoBR || maOoxData.mbDiagBLtoTR ) + maOoxData.maDiagonal.setBiffData( extractValue< sal_uInt8 >( nBorder2, 21, 4 ), extractValue< sal_uInt16 >( nBorder2, 14, 7 ) ); +} + +void Border::importCfRule( BiffInputStream& rStrm, sal_uInt32 nFlags ) +{ + OSL_ENSURE( mbDxf, "Border::importCfRule - missing conditional formatting flag" ); + OSL_ENSURE( getFlag( nFlags, BIFF_CFRULE_BORDERBLOCK ), "Border::importCfRule - missing border block flag" ); + sal_uInt16 nStyle; + sal_uInt32 nColor; + rStrm >> nStyle >> nColor; + rStrm.skip( 2 ); + maOoxData.maLeft.setBiffData( extractValue< sal_uInt8 >( nStyle, 0, 4 ), extractValue< sal_uInt16 >( nColor, 0, 7 ) ); + maOoxData.maRight.setBiffData( extractValue< sal_uInt8 >( nStyle, 4, 4 ), extractValue< sal_uInt16 >( nColor, 7, 7 ) ); + maOoxData.maTop.setBiffData( extractValue< sal_uInt8 >( nStyle, 8, 4 ), extractValue< sal_uInt16 >( nColor, 16, 7 ) ); + maOoxData.maBottom.setBiffData( extractValue< sal_uInt8 >( nStyle, 12, 4 ), extractValue< sal_uInt16 >( nColor, 23, 7 ) ); + maOoxData.maLeft.mbUsed = !getFlag( nFlags, BIFF_CFRULE_BORDER_LEFT ); + maOoxData.maRight.mbUsed = !getFlag( nFlags, BIFF_CFRULE_BORDER_RIGHT ); + maOoxData.maTop.mbUsed = !getFlag( nFlags, BIFF_CFRULE_BORDER_TOP ); + maOoxData.maBottom.mbUsed = !getFlag( nFlags, BIFF_CFRULE_BORDER_BOTTOM ); +} + +void Border::finalizeImport() +{ + maApiData.mbBorderUsed = maOoxData.maLeft.mbUsed || maOoxData.maRight.mbUsed || maOoxData.maTop.mbUsed || maOoxData.maBottom.mbUsed; + maApiData.mbDiagUsed = maOoxData.maDiagonal.mbUsed; + + maApiData.maBorder.IsLeftLineValid = convertBorderLine( maApiData.maBorder.LeftLine, maOoxData.maLeft ); + maApiData.maBorder.IsRightLineValid = convertBorderLine( maApiData.maBorder.RightLine, maOoxData.maRight ); + maApiData.maBorder.IsTopLineValid = convertBorderLine( maApiData.maBorder.TopLine, maOoxData.maTop ); + maApiData.maBorder.IsBottomLineValid = convertBorderLine( maApiData.maBorder.BottomLine, maOoxData.maBottom ); + + if( !mbDxf ) + { + maApiData.maBorder.IsVerticalLineValid = maApiData.maBorder.IsLeftLineValid || maApiData.maBorder.IsRightLineValid; + if( const BorderLine* pVertLine = lclGetThickerLine( maApiData.maBorder.LeftLine, maApiData.maBorder.IsLeftLineValid, maApiData.maBorder.RightLine, maApiData.maBorder.IsRightLineValid ) ) + maApiData.maBorder.VerticalLine = *pVertLine; + + maApiData.maBorder.IsHorizontalLineValid = maApiData.maBorder.IsTopLineValid || maApiData.maBorder.IsBottomLineValid; + if( const BorderLine* pHorLine = lclGetThickerLine( maApiData.maBorder.TopLine, maApiData.maBorder.IsTopLineValid, maApiData.maBorder.BottomLine, maApiData.maBorder.IsBottomLineValid ) ) + maApiData.maBorder.HorizontalLine = *pHorLine; + } + + if( maOoxData.mbDiagTLtoBR ) + convertBorderLine( maApiData.maTLtoBR, maOoxData.maDiagonal ); + if( maOoxData.mbDiagBLtoTR ) + convertBorderLine( maApiData.maBLtoTR, maOoxData.maDiagonal ); +} + +void Border::writeToPropertySet( PropertySet& rPropSet ) const +{ + getStylesPropertyHelper().writeBorderProperties( rPropSet, maApiData ); +} + +OoxBorderLineData* Border::getBorderLine( sal_Int32 nElement ) +{ + switch( nElement ) + { + case XLS_TOKEN( left ): return &maOoxData.maLeft; + case XLS_TOKEN( right ): return &maOoxData.maRight; + case XLS_TOKEN( top ): return &maOoxData.maTop; + case XLS_TOKEN( bottom ): return &maOoxData.maBottom; + case XLS_TOKEN( diagonal ): return &maOoxData.maDiagonal; + } + return 0; +} + +bool Border::convertBorderLine( BorderLine& rBorderLine, const OoxBorderLineData& rLineData ) +{ + rBorderLine.Color = getStyles().getColor( rLineData.maColor, API_RGB_BLACK ); + switch( rLineData.mnStyle ) + { + case XML_dashDot: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break; + case XML_dashDotDot: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break; + case XML_dashed: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break; + case XML_dotted: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break; + case XML_double: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN, API_LINE_THIN, API_LINE_THIN ); break; + case XML_hair: lclSetBorderLineWidth( rBorderLine, API_LINE_HAIR ); break; + case XML_medium: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break; + case XML_mediumDashDot: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break; + case XML_mediumDashDotDot: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break; + case XML_mediumDashed: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break; + case XML_none: lclSetBorderLineWidth( rBorderLine, API_LINE_NONE ); break; + case XML_slantDashDot: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break; + case XML_thick: lclSetBorderLineWidth( rBorderLine, API_LINE_THICK ); break; + case XML_thin: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break; + default: lclSetBorderLineWidth( rBorderLine, API_LINE_NONE ); break; + } + return rLineData.mbUsed; +} + + +// ============================================================================ + +OoxPatternFillData::OoxPatternFillData( bool bDxf ) : + maPatternColor( XML_indexed, OOX_COLOR_WINDOWTEXT ), + maFillColor( XML_indexed, OOX_COLOR_WINDOWBACK ), + mnPattern( XML_none ), + mbPattColorUsed( !bDxf ), + mbFillColorUsed( !bDxf ), + mbPatternUsed( !bDxf ) +{ +} + +void OoxPatternFillData::setBinPattern( sal_Int32 nPattern ) +{ + static const sal_Int32 spnPatternIds[] = { + XML_none, XML_solid, XML_mediumGray, XML_darkGray, + XML_lightGray, XML_darkHorizontal, XML_darkVertical, XML_darkDown, + XML_darkUp, XML_darkGrid, XML_darkTrellis, XML_lightHorizontal, + XML_lightVertical, XML_lightDown, XML_lightUp, XML_lightGrid, + XML_lightTrellis, XML_gray125, XML_gray0625 }; + mnPattern = STATIC_ARRAY_SELECT( spnPatternIds, nPattern, XML_none ); +} + +void OoxPatternFillData::setBiffData( sal_uInt16 nPatternColor, sal_uInt16 nFillColor, sal_uInt8 nPattern ) +{ + maPatternColor.set( XML_indexed, static_cast< sal_Int32 >( nPatternColor ) ); + maFillColor.set( XML_indexed, static_cast< sal_Int32 >( nFillColor ) ); + // patterns equal in BIFF and OOBIN + setBinPattern( nPattern ); +} + +// ---------------------------------------------------------------------------- + +OoxGradientFillData::OoxGradientFillData() : + mnType( XML_linear ), + mfAngle( 0.0 ), + mfLeft( 0.0 ), + mfRight( 0.0 ), + mfTop( 0.0 ), + mfBottom( 0.0 ) +{ +} + +void OoxGradientFillData::readGradient( RecordInputStream& rStrm ) +{ + sal_Int32 nType; + rStrm >> nType >> mfAngle >> mfLeft >> mfRight >> mfTop >> mfBottom; + static const sal_Int32 spnTypes[] = { XML_linear, XML_path }; + mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID ); +} + +void OoxGradientFillData::readGradientStop( RecordInputStream& rStrm, bool bDxf ) +{ + OoxColor aColor; + double fPosition; + if( bDxf ) + { + rStrm.skip( 2 ); + rStrm >> fPosition >> aColor; + } + else + { + rStrm >> aColor >> fPosition; + } + if( rStrm.isValid() && (fPosition >= 0.0) ) + maColors[ fPosition ] = aColor; +} + +// ============================================================================ + +namespace { + +inline sal_Int32 lclGetMixedColorComp( sal_Int32 nPatt, sal_Int32 nFill, sal_Int32 nAlpha ) +{ + return ((nPatt - nFill) * nAlpha) / 0x80 + nFill; +} + +sal_Int32 lclGetMixedColor( sal_Int32 nPattColor, sal_Int32 nFillColor, sal_Int32 nAlpha ) +{ + return + (lclGetMixedColorComp( nPattColor & 0xFF0000, nFillColor & 0xFF0000, nAlpha ) & 0xFF0000) | + (lclGetMixedColorComp( nPattColor & 0x00FF00, nFillColor & 0x00FF00, nAlpha ) & 0x00FF00) | + (lclGetMixedColorComp( nPattColor & 0x0000FF, nFillColor & 0x0000FF, nAlpha ) & 0x0000FF); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +Fill::Fill( const WorkbookHelper& rHelper, bool bDxf ) : + WorkbookHelper( rHelper ), + mbDxf( bDxf ) +{ +} + +bool Fill::isSupportedContext( sal_Int32 nElement, sal_Int32 nParentContext ) +{ + switch( nParentContext ) + { + case XLS_TOKEN( fill ): + return (nElement == XLS_TOKEN( patternFill )) || + (nElement == XLS_TOKEN( gradientFill )); + case XLS_TOKEN( patternFill ): + return (nElement == XLS_TOKEN( fgColor )) || + (nElement == XLS_TOKEN( bgColor )); + case XLS_TOKEN( gradientFill ): + return (nElement == XLS_TOKEN( stop )); + case XLS_TOKEN( stop ): + return (nElement == XLS_TOKEN( color )); + } + return false; +} + +void Fill::importPatternFill( const AttributeList& rAttribs ) +{ + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->mnPattern = rAttribs.getToken( XML_patternType, XML_none ); + if( mbDxf ) + mxOoxPattData->mbPatternUsed = rAttribs.hasAttribute( XML_patternType ); +} + +void Fill::importFgColor( const AttributeList& rAttribs ) +{ + OSL_ENSURE( mxOoxPattData.get(), "Fill::importFgColor - missing pattern data" ); + if( mxOoxPattData.get() ) + { + mxOoxPattData->maPatternColor.importColor( rAttribs ); + mxOoxPattData->mbPattColorUsed = true; + } +} + +void Fill::importBgColor( const AttributeList& rAttribs ) +{ + OSL_ENSURE( mxOoxPattData.get(), "Fill::importBgColor - missing pattern data" ); + if( mxOoxPattData.get() ) + { + mxOoxPattData->maFillColor.importColor( rAttribs ); + mxOoxPattData->mbFillColorUsed = true; + } +} + +void Fill::importGradientFill( const AttributeList& rAttribs ) +{ + mxOoxGradData.reset( new OoxGradientFillData ); + mxOoxGradData->mnType = rAttribs.getToken( XML_type, XML_linear ); + mxOoxGradData->mfAngle = rAttribs.getDouble( XML_degree, 0.0 ); + mxOoxGradData->mfLeft = rAttribs.getDouble( XML_left, 0.0 ); + mxOoxGradData->mfRight = rAttribs.getDouble( XML_right, 0.0 ); + mxOoxGradData->mfTop = rAttribs.getDouble( XML_top, 0.0 ); + mxOoxGradData->mfBottom = rAttribs.getDouble( XML_bottom, 0.0 ); +} + +void Fill::importColor( const AttributeList& rAttribs, double fPosition ) +{ + OSL_ENSURE( mxOoxGradData.get(), "Fill::importColor - missing gradient data" ); + if( mxOoxGradData.get() && (fPosition >= 0.0) ) + mxOoxGradData->maColors[ fPosition ].importColor( rAttribs ); +} + +void Fill::importFill( RecordInputStream& rStrm ) +{ + OSL_ENSURE( !mbDxf, "Fill::importFill - unexpected conditional formatting flag" ); + sal_Int32 nPattern = rStrm.readInt32(); + if( nPattern == OOBIN_FILL_GRADIENT ) + { + mxOoxGradData.reset( new OoxGradientFillData ); + sal_Int32 nStopCount; + rStrm.skip( 16 ); + mxOoxGradData->readGradient( rStrm ); + rStrm >> nStopCount; + for( sal_Int32 nStop = 0; (nStop < nStopCount) && rStrm.isValid(); ++nStop ) + mxOoxGradData->readGradientStop( rStrm, false ); + } + else + { + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->setBinPattern( nPattern ); + rStrm >> mxOoxPattData->maPatternColor >> mxOoxPattData->maFillColor; + } +} + +void Fill::importDxfPattern( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Fill::importDxfPattern - missing conditional formatting flag" ); + if( !mxOoxPattData ) + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->setBinPattern( rStrm.readuInt8() ); + mxOoxPattData->mbPatternUsed = true; +} + +void Fill::importDxfFgColor( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Fill::importDxfFgColor - missing conditional formatting flag" ); + if( !mxOoxPattData ) + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->maPatternColor.importColor( rStrm ); + mxOoxPattData->mbPattColorUsed = true; +} + +void Fill::importDxfBgColor( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Fill::importDxfBgColor - missing conditional formatting flag" ); + if( !mxOoxPattData ) + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->maFillColor.importColor( rStrm ); + mxOoxPattData->mbFillColorUsed = true; +} + +void Fill::importDxfGradient( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Fill::importDxfGradient - missing conditional formatting flag" ); + if( !mxOoxGradData ) + mxOoxGradData.reset( new OoxGradientFillData ); + mxOoxGradData->readGradient( rStrm ); +} + +void Fill::importDxfStop( RecordInputStream& rStrm ) +{ + OSL_ENSURE( mbDxf, "Fill::importDxfStop - missing conditional formatting flag" ); + if( !mxOoxGradData ) + mxOoxGradData.reset( new OoxGradientFillData ); + mxOoxGradData->readGradientStop( rStrm, true ); +} + +void Fill::setBiff2Data( sal_uInt8 nFlags ) +{ + OSL_ENSURE( !mbDxf, "Fill::setBiff2Data - unexpected conditional formatting flag" ); + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->setBiffData( + BIFF2_COLOR_BLACK, + BIFF2_COLOR_WHITE, + getFlagValue( nFlags, BIFF2_XF_BACKGROUND, BIFF_PATT_125, BIFF_PATT_NONE ) ); +} + +void Fill::setBiff3Data( sal_uInt16 nArea ) +{ + OSL_ENSURE( !mbDxf, "Fill::setBiff3Data - unexpected conditional formatting flag" ); + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->setBiffData( + extractValue< sal_uInt16 >( nArea, 6, 5 ), + extractValue< sal_uInt16 >( nArea, 11, 5 ), + extractValue< sal_uInt8 >( nArea, 0, 6 ) ); +} + +void Fill::setBiff5Data( sal_uInt32 nArea ) +{ + OSL_ENSURE( !mbDxf, "Fill::setBiff5Data - unexpected conditional formatting flag" ); + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->setBiffData( + extractValue< sal_uInt16 >( nArea, 0, 7 ), + extractValue< sal_uInt16 >( nArea, 7, 7 ), + extractValue< sal_uInt8 >( nArea, 16, 6 ) ); +} + +void Fill::setBiff8Data( sal_uInt32 nBorder2, sal_uInt16 nArea ) +{ + OSL_ENSURE( !mbDxf, "Fill::setBiff8Data - unexpected conditional formatting flag" ); + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + mxOoxPattData->setBiffData( + extractValue< sal_uInt16 >( nArea, 0, 7 ), + extractValue< sal_uInt16 >( nArea, 7, 7 ), + extractValue< sal_uInt8 >( nBorder2, 26, 6 ) ); +} + +void Fill::importCfRule( BiffInputStream& rStrm, sal_uInt32 nFlags ) +{ + OSL_ENSURE( mbDxf, "Fill::importCfRule - missing conditional formatting flag" ); + OSL_ENSURE( getFlag( nFlags, BIFF_CFRULE_FILLBLOCK ), "Fill::importCfRule - missing fill block flag" ); + mxOoxPattData.reset( new OoxPatternFillData( mbDxf ) ); + sal_uInt32 nFillData; + rStrm >> nFillData; + mxOoxPattData->setBiffData( + extractValue< sal_uInt16 >( nFillData, 16, 7 ), + extractValue< sal_uInt16 >( nFillData, 23, 7 ), + extractValue< sal_uInt8 >( nFillData, 10, 6 ) ); + mxOoxPattData->mbPattColorUsed = !getFlag( nFlags, BIFF_CFRULE_FILL_PATTCOLOR ); + mxOoxPattData->mbFillColorUsed = !getFlag( nFlags, BIFF_CFRULE_FILL_FILLCOLOR ); + mxOoxPattData->mbPatternUsed = !getFlag( nFlags, BIFF_CFRULE_FILL_PATTERN ); +} + +void Fill::finalizeImport() +{ + if( mxOoxPattData.get() ) + { + // finalize the OOX data struct + OoxPatternFillData& rOoxData = *mxOoxPattData; + if( mbDxf ) + { + if( rOoxData.mbFillColorUsed && (!rOoxData.mbPatternUsed || (rOoxData.mnPattern == XML_solid)) ) + { + rOoxData.maPatternColor = rOoxData.maFillColor; + rOoxData.mnPattern = XML_solid; + rOoxData.mbPattColorUsed = rOoxData.mbPatternUsed = true; + } + else if( !rOoxData.mbFillColorUsed && rOoxData.mbPatternUsed && (rOoxData.mnPattern == XML_solid) ) + { + rOoxData.mbPatternUsed = false; + } + } + + // convert to API fill settings + maApiData.mbUsed = rOoxData.mbPatternUsed; + if( rOoxData.mnPattern == XML_none ) + { + maApiData.mnColor = API_RGB_TRANSPARENT; + maApiData.mbTransparent = true; + } + else + { + sal_Int32 nAlpha = 0x80; + switch( rOoxData.mnPattern ) + { + case XML_darkDown: nAlpha = 0x40; break; + case XML_darkGray: nAlpha = 0x60; break; + case XML_darkGrid: nAlpha = 0x40; break; + case XML_darkHorizontal: nAlpha = 0x40; break; + case XML_darkTrellis: nAlpha = 0x60; break; + case XML_darkUp: nAlpha = 0x40; break; + case XML_darkVertical: nAlpha = 0x40; break; + case XML_gray0625: nAlpha = 0x08; break; + case XML_gray125: nAlpha = 0x10; break; + case XML_lightDown: nAlpha = 0x20; break; + case XML_lightGray: nAlpha = 0x20; break; + case XML_lightGrid: nAlpha = 0x38; break; + case XML_lightHorizontal: nAlpha = 0x20; break; + case XML_lightTrellis: nAlpha = 0x30; break; + case XML_lightUp: nAlpha = 0x20; break; + case XML_lightVertical: nAlpha = 0x20; break; + case XML_mediumGray: nAlpha = 0x40; break; + case XML_solid: nAlpha = 0x80; break; + } + + if( !rOoxData.mbPattColorUsed ) + rOoxData.maPatternColor.set( XML_auto, 0 ); + sal_Int32 nPattColor = getStyles().getColor( + rOoxData.maPatternColor, ThemeBuffer::getSystemWindowTextColor() ); + + if( !rOoxData.mbFillColorUsed ) + rOoxData.maFillColor.set( XML_auto, 0 ); + sal_Int32 nFillColor = getStyles().getColor( + rOoxData.maFillColor, ThemeBuffer::getSystemWindowColor() ); + + maApiData.mnColor = lclGetMixedColor( nPattColor, nFillColor, nAlpha ); + maApiData.mbTransparent = false; + } + } + else if( mxOoxGradData.get() && !mxOoxGradData->maColors.empty() ) + { + OoxGradientFillData& rOoxData = *mxOoxGradData; + maApiData.mbUsed = true; // no support for differential attributes + OoxGradientFillData::OoxColorMap::const_iterator aIt = rOoxData.maColors.begin(); + OSL_ENSURE( !aIt->second.isAuto(), "Fill::finalizeImport - automatic gradient color" ); + maApiData.mnColor = getStyles().getColor( aIt->second, API_RGB_TRANSPARENT ); + if( ++aIt != rOoxData.maColors.end() ) + { + OSL_ENSURE( !aIt->second.isAuto(), "Fill::finalizeImport - automatic gradient color" ); + sal_Int32 nEndColor = getStyles().getColor( aIt->second, API_RGB_TRANSPARENT ); + maApiData.mnColor = lclGetMixedColor( maApiData.mnColor, nEndColor, 0x40 ); + maApiData.mbTransparent = false; + } + } +} + +void Fill::writeToPropertySet( PropertySet& rPropSet ) const +{ + getStylesPropertyHelper().writeSolidFillProperties( rPropSet, maApiData ); +} + +// ============================================================================ + +OoxXfData::OoxXfData() : + mnStyleXfId( -1 ), + mnFontId( -1 ), + mnNumFmtId( -1 ), + mnBorderId( -1 ), + mnFillId( -1 ), + mbCellXf( true ), + mbFontUsed( false ), + mbNumFmtUsed( false ), + mbAlignUsed( false ), + mbProtUsed( false ), + mbBorderUsed( false ), + mbAreaUsed( false ) +{ +} + +// ============================================================================ + +Xf::Xf( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maAlignment( rHelper ), + maProtection( rHelper ) +{ +} + +void Xf::setAllUsedFlags( bool bUsed ) +{ + maOoxData.mbAlignUsed = maOoxData.mbProtUsed = maOoxData.mbFontUsed = + maOoxData.mbNumFmtUsed = maOoxData.mbBorderUsed = maOoxData.mbAreaUsed = bUsed; +} + +void Xf::importXf( const AttributeList& rAttribs, bool bCellXf ) +{ + maOoxData.mbCellXf = bCellXf; + maOoxData.mnStyleXfId = rAttribs.getInteger( XML_xfId, -1 ); + maOoxData.mnFontId = rAttribs.getInteger( XML_fontId, -1 ); + maOoxData.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, -1 ); + maOoxData.mnBorderId = rAttribs.getInteger( XML_borderId, -1 ); + maOoxData.mnFillId = rAttribs.getInteger( XML_fillId, -1 ); + + /* Default value of the apply*** attributes is dependent on context: + true in cellStyleXfs element, false in cellXfs element... */ + maOoxData.mbAlignUsed = rAttribs.getBool( XML_applyAlignment, !maOoxData.mbCellXf ); + maOoxData.mbProtUsed = rAttribs.getBool( XML_applyProtection, !maOoxData.mbCellXf ); + maOoxData.mbFontUsed = rAttribs.getBool( XML_applyFont, !maOoxData.mbCellXf ); + maOoxData.mbNumFmtUsed = rAttribs.getBool( XML_applyNumberFormat, !maOoxData.mbCellXf ); + maOoxData.mbBorderUsed = rAttribs.getBool( XML_applyBorder, !maOoxData.mbCellXf ); + maOoxData.mbAreaUsed = rAttribs.getBool( XML_applyFill, !maOoxData.mbCellXf ); +} + +void Xf::importAlignment( const AttributeList& rAttribs ) +{ + maAlignment.importAlignment( rAttribs ); +} + +void Xf::importProtection( const AttributeList& rAttribs ) +{ + maProtection.importProtection( rAttribs ); +} + +void Xf::importXf( RecordInputStream& rStrm, bool bCellXf ) +{ + maOoxData.mbCellXf = bCellXf; + maOoxData.mnStyleXfId = rStrm.readuInt16(); + maOoxData.mnNumFmtId = rStrm.readuInt16(); + maOoxData.mnFontId = rStrm.readuInt16(); + maOoxData.mnFillId = rStrm.readuInt16(); + maOoxData.mnBorderId = rStrm.readuInt16(); + sal_uInt32 nFlags = rStrm.readuInt32(); + maAlignment.setBinData( nFlags ); + maProtection.setBinData( nFlags ); + // used flags, see comments in Xf::setBiffUsedFlags() + sal_uInt16 nUsedFlags = rStrm.readuInt16(); + maOoxData.mbFontUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, OOBIN_XF_FONT_USED ); + maOoxData.mbNumFmtUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, OOBIN_XF_NUMFMT_USED ); + maOoxData.mbAlignUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, OOBIN_XF_ALIGN_USED ); + maOoxData.mbProtUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, OOBIN_XF_PROT_USED ); + maOoxData.mbBorderUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, OOBIN_XF_BORDER_USED ); + maOoxData.mbAreaUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, OOBIN_XF_AREA_USED ); +} + +void Xf::importXf( BiffInputStream& rStrm ) +{ + BorderRef xBorder = getStyles().createBorder( &maOoxData.mnBorderId ); + FillRef xFill = getStyles().createFill( &maOoxData.mnFillId ); + + switch( getBiff() ) + { + case BIFF2: + { + sal_uInt8 nFontId, nNumFmtId, nFlags; + rStrm >> nFontId; + rStrm.skip( 1 ); + rStrm >> nNumFmtId >> nFlags; + + // only cell XFs in BIFF2, no parent style, used flags always true + setAllUsedFlags( true ); + + // attributes + maAlignment.setBiff2Data( nFlags ); + maProtection.setBiff2Data( nNumFmtId ); + xBorder->setBiff2Data( nFlags ); + xFill->setBiff2Data( nFlags ); + maOoxData.mnFontId = static_cast< sal_Int32 >( nFontId ); + maOoxData.mnNumFmtId = static_cast< sal_Int32 >( nNumFmtId & BIFF2_XF_VALFMT_MASK ); + } + break; + + case BIFF3: + { + sal_uInt32 nBorder; + sal_uInt16 nTypeProt, nAlign, nArea; + sal_uInt8 nFontId, nNumFmtId; + rStrm >> nFontId >> nNumFmtId >> nTypeProt >> nAlign >> nArea >> nBorder; + + // XF type/parent + maOoxData.mbCellXf = !getFlag( nTypeProt, BIFF_XF_STYLE ); // new in BIFF3 + maOoxData.mnStyleXfId = extractValue< sal_Int32 >( nAlign, 4, 12 ); // new in BIFF3 + // attribute used flags + setBiffUsedFlags( extractValue< sal_uInt8 >( nTypeProt, 10, 6 ) ); // new in BIFF3 + + // attributes + maAlignment.setBiff3Data( nAlign ); + maProtection.setBiff3Data( nTypeProt ); + xBorder->setBiff3Data( nBorder ); + xFill->setBiff3Data( nArea ); + maOoxData.mnFontId = static_cast< sal_Int32 >( nFontId ); + maOoxData.mnNumFmtId = static_cast< sal_Int32 >( nNumFmtId ); + } + break; + + case BIFF4: + { + sal_uInt32 nBorder; + sal_uInt16 nTypeProt, nAlign, nArea; + sal_uInt8 nFontId, nNumFmtId; + rStrm >> nFontId >> nNumFmtId >> nTypeProt >> nAlign >> nArea >> nBorder; + + // XF type/parent + maOoxData.mbCellXf = !getFlag( nTypeProt, BIFF_XF_STYLE ); + maOoxData.mnStyleXfId = extractValue< sal_Int32 >( nTypeProt, 4, 12 ); + // attribute used flags + setBiffUsedFlags( extractValue< sal_uInt8 >( nAlign, 10, 6 ) ); + + // attributes + maAlignment.setBiff4Data( nAlign ); + maProtection.setBiff3Data( nTypeProt ); + xBorder->setBiff3Data( nBorder ); + xFill->setBiff3Data( nArea ); + maOoxData.mnFontId = static_cast< sal_Int32 >( nFontId ); + maOoxData.mnNumFmtId = static_cast< sal_Int32 >( nNumFmtId ); + } + break; + + case BIFF5: + { + sal_uInt32 nArea, nBorder; + sal_uInt16 nFontId, nNumFmtId, nTypeProt, nAlign; + rStrm >> nFontId >> nNumFmtId >> nTypeProt >> nAlign >> nArea >> nBorder; + + // XF type/parent + maOoxData.mbCellXf = !getFlag( nTypeProt, BIFF_XF_STYLE ); + maOoxData.mnStyleXfId = extractValue< sal_Int32 >( nTypeProt, 4, 12 ); + // attribute used flags + setBiffUsedFlags( extractValue< sal_uInt8 >( nAlign, 10, 6 ) ); + + // attributes + maAlignment.setBiff5Data( nAlign ); + maProtection.setBiff3Data( nTypeProt ); + xBorder->setBiff5Data( nBorder, nArea ); + xFill->setBiff5Data( nArea ); + maOoxData.mnFontId = static_cast< sal_Int32 >( nFontId ); + maOoxData.mnNumFmtId = static_cast< sal_Int32 >( nNumFmtId ); + } + break; + + case BIFF8: + { + sal_uInt32 nBorder1, nBorder2; + sal_uInt16 nFontId, nNumFmtId, nTypeProt, nAlign, nMiscAttrib, nArea; + rStrm >> nFontId >> nNumFmtId >> nTypeProt >> nAlign >> nMiscAttrib >> nBorder1 >> nBorder2 >> nArea; + + // XF type/parent + maOoxData.mbCellXf = !getFlag( nTypeProt, BIFF_XF_STYLE ); + maOoxData.mnStyleXfId = extractValue< sal_Int32 >( nTypeProt, 4, 12 ); + // attribute used flags + setBiffUsedFlags( extractValue< sal_uInt8 >( nMiscAttrib, 10, 6 ) ); + + // attributes + maAlignment.setBiff8Data( nAlign, nMiscAttrib ); + maProtection.setBiff3Data( nTypeProt ); + xBorder->setBiff8Data( nBorder1, nBorder2 ); + xFill->setBiff8Data( nBorder2, nArea ); + maOoxData.mnFontId = static_cast< sal_Int32 >( nFontId ); + maOoxData.mnNumFmtId = static_cast< sal_Int32 >( nNumFmtId ); + } + break; + + case BIFF_UNKNOWN: break; + } +} + +void Xf::finalizeImport() +{ + // alignment and protection + maAlignment.finalizeImport(); + maProtection.finalizeImport(); + // update used flags from cell style + if( maOoxData.mbCellXf ) + if( const Xf* pStyleXf = getStyles().getStyleXf( maOoxData.mnStyleXfId ).get() ) + updateUsedFlags( *pStyleXf ); +} + +FontRef Xf::getFont() const +{ + return getStyles().getFont( maOoxData.mnFontId ); +} + +bool Xf::hasAnyUsedFlags() const +{ + return + maOoxData.mbAlignUsed || maOoxData.mbProtUsed || maOoxData.mbFontUsed || + maOoxData.mbNumFmtUsed || maOoxData.mbBorderUsed || maOoxData.mbAreaUsed; +} + +void Xf::writeToPropertySet( PropertySet& rPropSet ) const +{ + StylesBuffer& rStyles = getStyles(); + + // create and set cell style + if( maOoxData.mbCellXf ) + { + const OUString& rStyleName = rStyles.createCellStyle( maOoxData.mnStyleXfId ); + rPropSet.setProperty( CREATE_OUSTRING( "CellStyle" ), rStyleName ); + } + + if( maOoxData.mbFontUsed ) + rStyles.writeFontToPropertySet( rPropSet, maOoxData.mnFontId ); + if( maOoxData.mbNumFmtUsed ) + rStyles.writeNumFmtToPropertySet( rPropSet, maOoxData.mnNumFmtId ); + if( maOoxData.mbAlignUsed ) + maAlignment.writeToPropertySet( rPropSet ); + if( maOoxData.mbProtUsed ) + maProtection.writeToPropertySet( rPropSet ); + if( maOoxData.mbBorderUsed ) + rStyles.writeBorderToPropertySet( rPropSet, maOoxData.mnBorderId ); + if( maOoxData.mbAreaUsed ) + rStyles.writeFillToPropertySet( rPropSet, maOoxData.mnFillId ); +} + +void Xf::setBiffUsedFlags( sal_uInt8 nUsedFlags ) +{ + /* Notes about finding the used flags: + - In cell XFs a *set* bit means a used attribute. + - In style XFs a *cleared* bit means a used attribute. + The boolean flags always store true, if the attribute is used. + The "maOoxData.mbCellXf == getFlag(...)" construct evaluates to true in + both mentioned cases: cell XF and set bit; or style XF and cleared bit. + */ + maOoxData.mbFontUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, BIFF_XF_FONT_USED ); + maOoxData.mbNumFmtUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, BIFF_XF_NUMFMT_USED ); + maOoxData.mbAlignUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, BIFF_XF_ALIGN_USED ); + maOoxData.mbProtUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, BIFF_XF_PROT_USED ); + maOoxData.mbBorderUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, BIFF_XF_BORDER_USED ); + maOoxData.mbAreaUsed = maOoxData.mbCellXf == getFlag( nUsedFlags, BIFF_XF_AREA_USED ); +} + +void Xf::updateUsedFlags( const Xf& rStyleXf ) +{ + /* Enables the used flags, if the formatting attributes differ from the + passed style XF. In cell XFs Excel uses the cell attributes, if they + differ from the parent style XF. + #109899# ...or if the respective flag is not set in parent style XF. + */ + const OoxXfData& rStyleData = rStyleXf.maOoxData; + if( !maOoxData.mbFontUsed ) + maOoxData.mbFontUsed = !rStyleData.mbFontUsed || (maOoxData.mnFontId != rStyleData.mnFontId); + if( !maOoxData.mbNumFmtUsed ) + maOoxData.mbNumFmtUsed = !rStyleData.mbNumFmtUsed || (maOoxData.mnNumFmtId != rStyleData.mnNumFmtId); + if( !maOoxData.mbAlignUsed ) + maOoxData.mbAlignUsed = !rStyleData.mbAlignUsed || !(maAlignment.getApiData() == rStyleXf.maAlignment.getApiData()); + if( !maOoxData.mbProtUsed ) + maOoxData.mbProtUsed = !rStyleData.mbProtUsed || !(maProtection.getApiData() == rStyleXf.maProtection.getApiData()); + if( !maOoxData.mbBorderUsed ) + maOoxData.mbBorderUsed = !rStyleData.mbBorderUsed || (maOoxData.mnBorderId != rStyleData.mnBorderId); + if( !maOoxData.mbAreaUsed ) + maOoxData.mbAreaUsed = !rStyleData.mbAreaUsed || (maOoxData.mnFillId != rStyleData.mnFillId); +} + +// ============================================================================ + +Dxf::Dxf( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +FontRef Dxf::importFont( const AttributeList& ) +{ + createFont( true ); + return mxFont; +} + +void Dxf::importNumFmt( const AttributeList& rAttribs ) +{ + mxNumFmt = getStyles().importNumFmt( rAttribs ); +} + +void Dxf::importAlignment( const AttributeList& rAttribs ) +{ + mxAlignment.reset( new Alignment( *this ) ); + mxAlignment->importAlignment( rAttribs ); +} + +void Dxf::importProtection( const AttributeList& rAttribs ) +{ + mxProtection.reset( new Protection( *this ) ); + mxProtection->importProtection( rAttribs ); +} + +BorderRef Dxf::importBorder( const AttributeList& rAttribs ) +{ + createBorder( true ); + mxBorder->importBorder( rAttribs ); + return mxBorder; +} + +FillRef Dxf::importFill( const AttributeList& ) +{ + createFill( true ); + return mxFill; +} + +void Dxf::importDxf( RecordInputStream& rStrm ) +{ + sal_Int32 nNumFmtId = -1; + OUString aFmtCode; + sal_uInt16 nRecCount; + rStrm.skip( 4 ); // flags + rStrm >> nRecCount; + for( sal_uInt16 nRec = 0; rStrm.isValid() && (nRec < nRecCount); ++nRec ) + { + sal_uInt16 nSubRecId, nSubRecSize; + sal_Int32 nRecEnd = rStrm.getRecPos(); + rStrm >> nSubRecId >> nSubRecSize; + nRecEnd += nSubRecSize; + switch( nSubRecId ) + { + case OOBIN_DXF_FILL_PATTERN: createFill( false ); mxFill->importDxfPattern( rStrm ); break; + case OOBIN_DXF_FILL_FGCOLOR: createFill( false ); mxFill->importDxfFgColor( rStrm ); break; + case OOBIN_DXF_FILL_BGCOLOR: createFill( false ); mxFill->importDxfBgColor( rStrm ); break; + case OOBIN_DXF_FILL_GRADIENT: createFill( false ); mxFill->importDxfGradient( rStrm ); break; + case OOBIN_DXF_FILL_STOP: createFill( false ); mxFill->importDxfStop( rStrm ); break; + case OOBIN_DXF_FONT_COLOR: createFont( false ); mxFont->importDxfColor( rStrm ); break; + case OOBIN_DXF_BORDER_TOP: createBorder( false ); mxBorder->importDxfBorder( XLS_TOKEN( top ), rStrm ); break; + case OOBIN_DXF_BORDER_BOTTOM: createBorder( false ); mxBorder->importDxfBorder( XLS_TOKEN( bottom ), rStrm ); break; + case OOBIN_DXF_BORDER_LEFT: createBorder( false ); mxBorder->importDxfBorder( XLS_TOKEN( left ), rStrm ); break; + case OOBIN_DXF_BORDER_RIGHT: createBorder( false ); mxBorder->importDxfBorder( XLS_TOKEN( right ), rStrm ); break; + case OOBIN_DXF_FONT_NAME: createFont( false ); mxFont->importDxfName( rStrm ); break; + case OOBIN_DXF_FONT_WEIGHT: createFont( false ); mxFont->importDxfWeight( rStrm ); break; + case OOBIN_DXF_FONT_UNDERLINE: createFont( false ); mxFont->importDxfUnderline( rStrm ); break; + case OOBIN_DXF_FONT_ESCAPEMENT: createFont( false ); mxFont->importDxfEscapement( rStrm ); break; + case OOBIN_DXF_FONT_ITALIC: createFont( false ); mxFont->importDxfFlag( XML_i, rStrm ); break; + case OOBIN_DXF_FONT_STRIKE: createFont( false ); mxFont->importDxfFlag( XML_strike, rStrm ); break; + case OOBIN_DXF_FONT_OUTLINE: createFont( false ); mxFont->importDxfFlag( XML_outline, rStrm ); break; + case OOBIN_DXF_FONT_SHADOW: createFont( false ); mxFont->importDxfFlag( XML_shadow, rStrm ); break; + case OOBIN_DXF_FONT_HEIGHT: createFont( false ); mxFont->importDxfHeight( rStrm ); break; + case OOBIN_DXF_FONT_SCHEME: createFont( false ); mxFont->importDxfScheme( rStrm ); break; + case OOBIN_DXF_NUMFMT_CODE: aFmtCode = rStrm.readString( false ); break; + case OOBIN_DXF_NUMFMT_ID: nNumFmtId = rStrm.readuInt16(); break; + } + rStrm.seek( nRecEnd ); + } + OSL_ENSURE( rStrm.isValid() && (rStrm.getRecLeft() == 0), "Dxf::importDxf - unexpected remaining data" ); + mxNumFmt = getStyles().createNumFmt( nNumFmtId, aFmtCode ); +} + +void Dxf::importCfRule( BiffInputStream& rStrm, sal_uInt32 nFlags ) +{ + if( getFlag( nFlags, BIFF_CFRULE_FONTBLOCK ) ) + { + createFont( true ); + mxFont->importCfRule( rStrm ); + } + if( getFlag( nFlags, BIFF_CFRULE_ALIGNBLOCK ) ) + { + rStrm.skip( 8 ); + } + if( getFlag( nFlags, BIFF_CFRULE_BORDERBLOCK ) ) + { + createBorder( true ); + mxBorder->importCfRule( rStrm, nFlags ); + } + if( getFlag( nFlags, BIFF_CFRULE_FILLBLOCK ) ) + { + createFill( true ); + mxFill->importCfRule( rStrm, nFlags ); + } + if( getFlag( nFlags, BIFF_CFRULE_PROTBLOCK ) ) + { + rStrm.skip( 2 ); + } +} + +void Dxf::finalizeImport() +{ + if( mxFont.get() ) + mxFont->finalizeImport(); + // number format already finalized by the number formats buffer + if( mxAlignment.get() ) + mxAlignment->finalizeImport(); + if( mxProtection.get() ) + mxProtection->finalizeImport(); + if( mxBorder.get() ) + mxBorder->finalizeImport(); + if( mxFill.get() ) + mxFill->finalizeImport(); +} + +const OUString& Dxf::createDxfStyle( sal_Int32 nDxfId ) +{ + if( maFinalName.getLength() == 0 ) + { + maFinalName = OUStringBuffer( CREATE_OUSTRING( "ConditionalStyle_" ) ).append( nDxfId + 1 ).makeStringAndClear(); + Reference< XStyle > xStyle = createStyleObject( maFinalName, false ); + // write style formatting properties + PropertySet aPropSet( xStyle ); + if( mxFont.get() ) + mxFont->writeToPropertySet( aPropSet, FONT_PROPTYPE_CELL ); + if( mxNumFmt.get() ) + mxNumFmt->writeToPropertySet( aPropSet ); + if( mxAlignment.get() ) + mxAlignment->writeToPropertySet( aPropSet ); + if( mxProtection.get() ) + mxProtection->writeToPropertySet( aPropSet ); + if( mxBorder.get() ) + mxBorder->writeToPropertySet( aPropSet ); + if( mxFill.get() ) + mxFill->writeToPropertySet( aPropSet ); + } + return maFinalName; +} + +void Dxf::createFont( bool bAlwaysNew ) +{ + if( bAlwaysNew || !mxFont ) + mxFont.reset( new Font( *this, true ) ); +} + +void Dxf::createBorder( bool bAlwaysNew ) +{ + if( bAlwaysNew || !mxBorder ) + mxBorder.reset( new Border( *this, true ) ); +} + +void Dxf::createFill( bool bAlwaysNew ) +{ + if( bAlwaysNew || !mxFill ) + mxFill.reset( new Fill( *this, true ) ); +} + +// ============================================================================ + +namespace { + +const sal_Char* const spcLegacyStyleNamePrefix = "Excel_BuiltIn_"; +const sal_Char* const sppcLegacyStyleNames[] = +{ +#if OOX_XLS_USE_DEFAULT_STYLE + "", // use existing "Default" style +#else + "Normal", +#endif + "RowLevel_", // outline level will be appended + "ColumnLevel_", // outline level will be appended + "Comma", + "Currency", + "Percent", + "Comma_0", // new in BIFF4 + "Currency_0", + "Hyperlink", // new in BIFF8 + "Followed_Hyperlink" +}; +const sal_Int32 snLegacyStyleNamesCount = static_cast< sal_Int32 >( STATIC_ARRAY_SIZE( sppcLegacyStyleNames ) ); + +const sal_Char* const spcStyleNamePrefix = "Excel Built-in "; +const sal_Char* const sppcStyleNames[] = +{ +#if OOX_XLS_USE_DEFAULT_STYLE + "", // use existing "Default" style +#else + "Normal", +#endif + "RowLevel_", // outline level will be appended + "ColLevel_", // outline level will be appended + "Comma", + "Currency", + "Percent", + "Comma [0]", // new in BIFF4 + "Currency [0]", + "Hyperlink", // new in BIFF8 + "Followed Hyperlink", + "Note", // new in OOX + "Warning Text", + "", + "", + "", + "Title", + "Heading 1", + "Heading 2", + "Heading 3", + "Heading 4", + "Input", + "Output", + "Calculation", + "Check Cell", + "Linked Cell", + "Total", + "Good", + "Bad", + "Neutral", + "Accent1", + "20% - Accent1", + "40% - Accent1", + "60% - Accent1", + "Accent2", + "20% - Accent2", + "40% - Accent2", + "60% - Accent2", + "Accent3", + "20% - Accent3", + "40% - Accent3", + "60% - Accent3", + "Accent4", + "20% - Accent4", + "40% - Accent4", + "60% - Accent4", + "Accent5", + "20% - Accent5", + "40% - Accent5", + "60% - Accent5", + "Accent6", + "20% - Accent6", + "40% - Accent6", + "60% - Accent6", + "Explanatory Text" +}; +const sal_Int32 snStyleNamesCount = static_cast< sal_Int32 >( STATIC_ARRAY_SIZE( sppcStyleNames ) ); + +#if OOX_XLS_USE_DEFAULT_STYLE +const sal_Char* const spcDefaultStyleName = "Default"; +#endif + +OUString lclGetBuiltinStyleName( sal_Int32 nBuiltinId, const OUString& rName, sal_Int32 nLevel = 0 ) +{ + OUStringBuffer aStyleName; + OSL_ENSURE( (0 <= nBuiltinId) && (nBuiltinId < snStyleNamesCount), "lclGetBuiltinStyleName - unknown builtin style" ); +#if OOX_XLS_USE_DEFAULT_STYLE + if( nBuiltinId == OOX_STYLE_NORMAL ) // "Normal" becomes "Default" style + { + aStyleName.appendAscii( spcDefaultStyleName ); + } + else + { +#endif + aStyleName.appendAscii( spcStyleNamePrefix ); + if( (0 <= nBuiltinId) && (nBuiltinId < snStyleNamesCount) && (sppcStyleNames[ nBuiltinId ][ 0 ] != 0) ) + aStyleName.appendAscii( sppcStyleNames[ nBuiltinId ] ); + else if( rName.getLength() > 0 ) + aStyleName.append( rName ); + else + aStyleName.append( nBuiltinId ); + if( (nBuiltinId == OOX_STYLE_ROWLEVEL) || (nBuiltinId == OOX_STYLE_COLLEVEL) ) + aStyleName.append( nLevel ); +#if OOX_XLS_USE_DEFAULT_STYLE + } +#endif + return aStyleName.makeStringAndClear(); +} + +bool lclIsBuiltinStyleName( const OUString& rStyleName, sal_Int32* pnBuiltinId, sal_Int32* pnNextChar ) +{ +#if OOX_XLS_USE_DEFAULT_STYLE + // "Default" becomes "Normal" + if( rStyleName.equalsIgnoreAsciiCaseAscii( spcDefaultStyleName ) ) + { + if( pnBuiltinId ) *pnBuiltinId = OOX_STYLE_NORMAL; + if( pnNextChar ) *pnNextChar = rStyleName.getLength(); + return true; + } +#endif + + // try the other builtin styles + OUString aPrefix = OUString::createFromAscii( spcStyleNamePrefix ); + sal_Int32 nPrefixLen = aPrefix.getLength(); + sal_Int32 nFoundId = 0; + sal_Int32 nNextChar = 0; + if( rStyleName.matchIgnoreAsciiCase( aPrefix ) ) + { + OUString aShortName; + for( sal_Int32 nId = 0; nId < snStyleNamesCount; ++nId ) + { +#if OOX_XLS_USE_DEFAULT_STYLE + if( nId != OOX_STYLE_NORMAL ) + { +#endif + aShortName = OUString::createFromAscii( sppcStyleNames[ nId ] ); + if( rStyleName.matchIgnoreAsciiCase( aShortName, nPrefixLen ) && + (nNextChar < nPrefixLen + aShortName.getLength()) ) + { + nFoundId = nId; + nNextChar = nPrefixLen + aShortName.getLength(); + } +#if OOX_XLS_USE_DEFAULT_STYLE + } +#endif + } + } + + if( nNextChar > 0 ) + { + if( pnBuiltinId ) *pnBuiltinId = nFoundId; + if( pnNextChar ) *pnNextChar = nNextChar; + return true; + } + + if( pnBuiltinId ) *pnBuiltinId = -1; + if( pnNextChar ) *pnNextChar = 0; + return false; +} + +bool lclGetBuiltinStyleId( sal_Int32& rnBuiltinId, sal_Int32& rnLevel, const OUString& rStyleName ) +{ + sal_Int32 nBuiltinId; + sal_Int32 nNextChar; + if( lclIsBuiltinStyleName( rStyleName, &nBuiltinId, &nNextChar ) ) + { + if( (nBuiltinId == OOX_STYLE_ROWLEVEL) || (nBuiltinId == OOX_STYLE_COLLEVEL) ) + { + OUString aLevel = rStyleName.copy( nNextChar ); + sal_Int32 nLevel = aLevel.toInt32(); + if( (0 < nLevel) && (nLevel <= OOX_STYLE_LEVELCOUNT) ) + { + rnBuiltinId = nBuiltinId; + rnLevel = nLevel; + return true; + } + } + else if( rStyleName.getLength() == nNextChar ) + { + rnBuiltinId = nBuiltinId; + rnLevel = 0; + return true; + } + } + rnBuiltinId = -1; + rnLevel = 0; + return false; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +OoxCellStyleData::OoxCellStyleData() : + mnXfId( -1 ), + mnBuiltinId( -1 ), + mnLevel( 0 ), + mbBuiltin( false ), + mbCustom( false ), + mbHidden( false ) +{ +} + +bool OoxCellStyleData::isDefaultStyle() const +{ + return mbBuiltin && (mnBuiltinId == OOX_STYLE_NORMAL); +} + +OUString OoxCellStyleData::createStyleName() const +{ + return isBuiltin() ? lclGetBuiltinStyleName( mnBuiltinId, maName, mnLevel ) : maName; +} + +// ============================================================================ + +CellStyle::CellStyle( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void CellStyle::importCellStyle( const AttributeList& rAttribs ) +{ + maOoxData.maName = rAttribs.getString( XML_name ); + maOoxData.mnXfId = rAttribs.getInteger( XML_xfId, -1 ); + maOoxData.mnBuiltinId = rAttribs.getInteger( XML_builtinId, -1 ); + maOoxData.mnLevel = rAttribs.getInteger( XML_iLevel, 0 ); + maOoxData.mbBuiltin = rAttribs.hasAttribute( XML_builtinId ); + maOoxData.mbCustom = rAttribs.getBool( XML_customBuiltin, false ); + maOoxData.mbHidden = rAttribs.getBool( XML_hidden, false ); +} + +void CellStyle::importCellStyle( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> maOoxData.mnXfId >> nFlags; + maOoxData.mnBuiltinId = rStrm.readuInt8(); + maOoxData.mnLevel = rStrm.readuInt8(); + rStrm >> maOoxData.maName; + maOoxData.mbBuiltin = getFlag( nFlags, OOBIN_CELLSTYLE_BUILTIN ); + maOoxData.mbCustom = getFlag( nFlags, OOBIN_CELLSTYLE_CUSTOM ); + maOoxData.mbHidden = getFlag( nFlags, OOBIN_CELLSTYLE_HIDDEN ); +} + +void CellStyle::importStyle( BiffInputStream& rStrm ) +{ + sal_uInt16 nStyleXf; + rStrm >> nStyleXf; + maOoxData.mnXfId = static_cast< sal_Int32 >( nStyleXf & BIFF_STYLE_XFMASK ); + maOoxData.mbBuiltin = getFlag( nStyleXf, BIFF_STYLE_BUILTIN ); + if( maOoxData.mbBuiltin ) + { + maOoxData.mnBuiltinId = rStrm.readuInt8(); + maOoxData.mnLevel = rStrm.readuInt8(); + } + else + { + maOoxData.maName = (getBiff() == BIFF8) ? + rStrm.readUniString() : rStrm.readByteString( false, getTextEncoding() ); + } +} + +const OUString& CellStyle::createCellStyle( sal_Int32 nXfId, bool bSkipDefaultBuiltin ) +{ + if( maFinalName.getLength() == 0 ) + { + bool bBuiltin = maOoxData.isBuiltin(); + if( !bSkipDefaultBuiltin || !bBuiltin || maOoxData.mbCustom ) + { + // name of the style (generate unique name for builtin styles) + maFinalName = maOoxData.createStyleName(); + // #i1624# #i1768# ignore unnamed user styles + if( maFinalName.getLength() > 0 ) + { + Reference< XStyle > xStyle; +#if OOX_XLS_USE_DEFAULT_STYLE + // special handling for default style (do not create, but use existing) + if( isDefaultStyle() ) + { + /* Set all flags to true to have all properties in the style, + even if the used flags are not set (that's what Excel does). */ + if( Xf* pXf = getStyles().getStyleXf( nXfId ).get() ) + pXf->setAllUsedFlags( true ); + // use existing built-in style + xStyle = getStyleObject( maFinalName, false ); + } + else + { +#endif + /* Insert into cell styles collection, rename existing user styles, + if this is a built-in style, but do not do this in BIFF4 workspace + files, where built-in styles occur repeatedly. */ + bool bRenameExisting = bBuiltin && (getBiff() != BIFF4); + xStyle = createStyleObject( maFinalName, false, bRenameExisting ); +#if OOX_XLS_USE_DEFAULT_STYLE + } +#endif + + // write style formatting properties + PropertySet aPropSet( xStyle ); + getStyles().writeStyleXfToPropertySet( aPropSet, nXfId ); +#if OOX_XLS_USE_DEFAULT_STYLE +#else + if( !isDefaultStyle() && xStyle.is() ) + xStyle->setParentStyle( getStyles().getDefaultStyleName() ); +#endif + } + } + } + return maFinalName; +} + +// ============================================================================ + +namespace { + +sal_Int32 lclTintToColor( sal_Int32 nColor, double fTint ) +{ + if( nColor == 0x000000 ) + return 0x010101 * static_cast< sal_Int32 >( ::std::max( fTint, 0.0 ) * 255.0 ); + if( nColor == 0xFFFFFF ) + return 0x010101 * static_cast< sal_Int32 >( ::std::min( fTint + 1.0, 1.0 ) * 255.0 ); + + sal_Int32 nR = (nColor >> 16) & 0xFF; + sal_Int32 nG = (nColor >> 8) & 0xFF; + sal_Int32 nB = nColor & 0xFF; + + double fMean = (::std::min( ::std::min( nR, nG ), nB ) + ::std::max( ::std::max( nR, nG ), nB )) / 2.0; + double fTintTh = (fMean <= 127.5) ? ((127.5 - fMean) / (255.0 - fMean)) : (127.5 / fMean - 1.0); + if( (fTintTh < 0.0) || ((fTintTh == 0.0) && (fTint <= 0.0)) ) + { + double fTintMax = 255.0 / fMean - 1.0; + double fRTh = fTintTh / fTintMax * (255.0 - nR) + nR; + double fGTh = fTintTh / fTintMax * (255.0 - nG) + nG; + double fBTh = fTintTh / fTintMax * (255.0 - nB) + nB; + if( fTint <= fTintTh ) + { + double fFactor = (fTint + 1.0) / (fTintTh + 1.0); + nR = static_cast< sal_Int32 >( fFactor * fRTh + 0.5 ); + nG = static_cast< sal_Int32 >( fFactor * fGTh + 0.5 ); + nB = static_cast< sal_Int32 >( fFactor * fBTh + 0.5 ); + } + else + { + double fFactor = (fTint > 0.0) ? (fTint * fTintMax / fTintTh) : (fTint / fTintTh); + nR = static_cast< sal_Int32 >( fFactor * fRTh + (1.0 - fFactor) * nR + 0.5 ); + nG = static_cast< sal_Int32 >( fFactor * fGTh + (1.0 - fFactor) * nG + 0.5 ); + nB = static_cast< sal_Int32 >( fFactor * fBTh + (1.0 - fFactor) * nB + 0.5 ); + } + } + else + { + double fTintMin = fMean / (fMean - 255.0); + double fRTh = (1.0 - fTintTh / fTintMin) * nR; + double fGTh = (1.0 - fTintTh / fTintMin) * nG; + double fBTh = (1.0 - fTintTh / fTintMin) * nB; + if( fTint <= fTintTh ) + { + double fFactor = (fTint < 0.0) ? (fTint * -fTintMin / fTintTh) : (fTint / fTintTh); + nR = static_cast< sal_Int32 >( fFactor * fRTh + (1.0 - fFactor) * nR + 0.5 ); + nG = static_cast< sal_Int32 >( fFactor * fGTh + (1.0 - fFactor) * nG + 0.5 ); + nB = static_cast< sal_Int32 >( fFactor * fBTh + (1.0 - fFactor) * nB + 0.5 ); + } + else + { + double fFactor = (1.0 - fTint) / (1.0 - fTintTh); + nR = static_cast< sal_Int32 >( 255.5 - fFactor * (255.0 - fRTh) ); + nG = static_cast< sal_Int32 >( 255.5 - fFactor * (255.0 - fGTh) ); + nB = static_cast< sal_Int32 >( 255.5 - fFactor * (255.0 - fBTh) ); + } + } + + return (nR << 16) | (nG << 8) | nB; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +StylesBuffer::StylesBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maPalette( rHelper ), + maNumFmts( rHelper ), + maDefStyleName( lclGetBuiltinStyleName( OOX_STYLE_NORMAL, OUString() ) ), + mnDefStyleXf( -1 ) +{ +} + +FontRef StylesBuffer::createFont( sal_Int32* opnFontId ) +{ + if( opnFontId ) *opnFontId = static_cast< sal_Int32 >( maFonts.size() ); + FontRef xFont( new Font( *this, false ) ); + maFonts.push_back( xFont ); + return xFont; +} + +NumberFormatRef StylesBuffer::createNumFmt( sal_Int32 nNumFmtId, const OUString& rFmtCode ) +{ + return maNumFmts.createNumFmt( nNumFmtId, rFmtCode ); +} + +BorderRef StylesBuffer::createBorder( sal_Int32* opnBorderId ) +{ + if( opnBorderId ) *opnBorderId = static_cast< sal_Int32 >( maBorders.size() ); + BorderRef xBorder( new Border( *this, false ) ); + maBorders.push_back( xBorder ); + return xBorder; +} + +FillRef StylesBuffer::createFill( sal_Int32* opnFillId ) +{ + if( opnFillId ) *opnFillId = static_cast< sal_Int32 >( maFills.size() ); + FillRef xFill( new Fill( *this, false ) ); + maFills.push_back( xFill ); + return xFill; +} + +XfRef StylesBuffer::createCellXf( sal_Int32* opnXfId ) +{ + if( opnXfId ) *opnXfId = static_cast< sal_Int32 >( maCellXfs.size() ); + XfRef xXf( new Xf( *this ) ); + maCellXfs.push_back( xXf ); + return xXf; +} + +XfRef StylesBuffer::createStyleXf( sal_Int32* opnXfId ) +{ + if( opnXfId ) *opnXfId = static_cast< sal_Int32 >( maStyleXfs.size() ); + XfRef xXf( new Xf( *this ) ); + maStyleXfs.push_back( xXf ); + return xXf; +} + +DxfRef StylesBuffer::createDxf( sal_Int32* opnDxfId ) +{ + if( opnDxfId ) *opnDxfId = static_cast< sal_Int32 >( maDxfs.size() ); + DxfRef xDxf( new Dxf( *this ) ); + maDxfs.push_back( xDxf ); + return xDxf; +} + +void StylesBuffer::importPaletteColor( const AttributeList& rAttribs ) +{ + maPalette.importPaletteColor( rAttribs ); +} + +FontRef StylesBuffer::importFont( const AttributeList& ) +{ + return createFont(); +} + +NumberFormatRef StylesBuffer::importNumFmt( const AttributeList& rAttribs ) +{ + return maNumFmts.importNumFmt( rAttribs ); +} + +BorderRef StylesBuffer::importBorder( const AttributeList& rAttribs ) +{ + BorderRef xBorder = createBorder(); + xBorder->importBorder( rAttribs ); + return xBorder; +} + +FillRef StylesBuffer::importFill( const AttributeList& ) +{ + return createFill(); +} + +XfRef StylesBuffer::importXf( sal_Int32 nContext, const AttributeList& rAttribs ) +{ + XfRef xXf; + switch( nContext ) + { + case XLS_TOKEN( cellXfs ): + xXf = createCellXf(); + xXf->importXf( rAttribs, true ); + break; + case XLS_TOKEN( cellStyleXfs ): + xXf = createStyleXf(); + xXf->importXf( rAttribs, false ); + break; + } + return xXf; +} + +DxfRef StylesBuffer::importDxf( const AttributeList& ) +{ + return createDxf(); +} + +CellStyleRef StylesBuffer::importCellStyle( const AttributeList& rAttribs ) +{ + CellStyleRef xCellStyle( new CellStyle( *this ) ); + xCellStyle->importCellStyle( rAttribs ); + insertCellStyle( xCellStyle ); + return xCellStyle; +} + +void StylesBuffer::importPaletteColor( RecordInputStream& rStrm ) +{ + maPalette.importPaletteColor( rStrm ); +} + +void StylesBuffer::importFont( RecordInputStream& rStrm ) +{ + createFont()->importFont( rStrm ); +} + +void StylesBuffer::importNumFmt( RecordInputStream& rStrm ) +{ + maNumFmts.importNumFmt( rStrm ); +} + +void StylesBuffer::importBorder( RecordInputStream& rStrm ) +{ + createBorder()->importBorder( rStrm ); +} + +void StylesBuffer::importFill( RecordInputStream& rStrm ) +{ + createFill()->importFill( rStrm ); +} + +void StylesBuffer::importXf( sal_Int32 nContext, RecordInputStream& rStrm ) +{ + switch( nContext ) + { + case OOBIN_ID_CELLXFS: + createCellXf()->importXf( rStrm, true ); + break; + case OOBIN_ID_CELLSTYLEXFS: + createStyleXf()->importXf( rStrm, false ); + break; + } +} + +void StylesBuffer::importDxf( RecordInputStream& rStrm ) +{ + createDxf()->importDxf( rStrm ); +} + +void StylesBuffer::importCellStyle( RecordInputStream& rStrm ) +{ + CellStyleRef xCellStyle( new CellStyle( *this ) ); + xCellStyle->importCellStyle( rStrm ); + insertCellStyle( xCellStyle ); +} + +void StylesBuffer::importPalette( BiffInputStream& rStrm ) +{ + maPalette.importPalette( rStrm ); +} + +void StylesBuffer::importFont( BiffInputStream& rStrm ) +{ + /* Font with index 4 is not stored in BIFF. This means effectively, first + font in the BIFF file has index 0, fourth font has index 3, and fifth + font has index 5. Insert a dummy font to correctly map passed font + identifiers. */ + if( maFonts.size() == 4 ) + maFonts.push_back( maFonts.front() ); + + FontRef xFont = createFont(); + xFont->importFont( rStrm ); + + /* #i71033# Set stream text encoding from application font, if CODEPAGE + record is missing. Must be done now (not while finalizeImport() runs), + to be able to read all following byte strings correctly (e.g. cell + style names). */ + if( maFonts.size() == 1 ) + setAppFontEncoding( xFont->getFontEncoding() ); +} + +void StylesBuffer::importFontColor( BiffInputStream& rStrm ) +{ + if( !maFonts.empty() ) + maFonts.back()->importFontColor( rStrm ); +} + +void StylesBuffer::importFormat( BiffInputStream& rStrm ) +{ + maNumFmts.importFormat( rStrm ); +} + +void StylesBuffer::importXf( BiffInputStream& rStrm ) +{ + XfRef xXf( new Xf( *this ) ); + // store XF in both lists (except BIFF2 which does not support cell styles) + maCellXfs.push_back( xXf ); + if( getBiff() != BIFF2 ) + maStyleXfs.push_back( xXf ); + xXf->importXf( rStrm ); +} + +void StylesBuffer::importStyle( BiffInputStream& rStrm ) +{ + CellStyleRef xCellStyle( new CellStyle( *this ) ); + xCellStyle->importStyle( rStrm ); + insertCellStyle( xCellStyle ); +} + +void StylesBuffer::finalizeImport() +{ + // fonts first, are needed to finalize unit converter and XFs below + maFonts.forEachMem( &Font::finalizeImport ); + // finalize unit converter after default font is known + getUnitConverter().finalizeImport(); + // number formats + maNumFmts.finalizeImport(); + // borders and fills + maBorders.forEachMem( &Border::finalizeImport ); + maFills.forEachMem( &Fill::finalizeImport ); + + /* Style XFs and cell XFs. The BIFF format stores cell XFs and style XFs + mixed in a single list. The import filter has stored the XFs in both + lists to make the getStyleXf() function working correctly (e.g. for + retrieving the default font, see getDefaultFont() function), except for + BIFF2 which does not support cell styles at all. Therefore, if in BIFF + filter mode, we do not need to finalize the cell styles list. */ + if( getFilterType() == FILTER_OOX ) + maStyleXfs.forEachMem( &Xf::finalizeImport ); + maCellXfs.forEachMem( &Xf::finalizeImport ); + + // conditional formatting + maDxfs.forEachMem( &Dxf::finalizeImport ); + + // create the default cell style first + if( CellStyle* pDefStyle = maCellStyles.get( mnDefStyleXf ).get() ) + pDefStyle->createCellStyle( mnDefStyleXf ); + /* Create user-defined and modified builtin cell styles, passing true to + createStyleSheet() skips unchanged builtin styles. */ + for( CellStyleMap::iterator aIt = maCellStyles.begin(), aEnd = maCellStyles.end(); aIt != aEnd; ++aIt ) + aIt->second->createCellStyle( aIt->first, true ); +} + +sal_Int32 StylesBuffer::getColor( const OoxColor& rColor, sal_Int32 nAuto ) const +{ + sal_Int32 nColor = API_RGB_TRANSPARENT; + switch( rColor.mnType ) + { + case XML_auto: + nColor = nAuto; + break; + case XML_rgb: + nColor = rColor.mnValue & 0xFFFFFF; + break; + case XML_theme: + nColor = getTheme().getColorByIndex( rColor.mnValue ); + break; + case XML_indexed: + nColor = maPalette.getColor( rColor.mnValue ); + break; + default: + OSL_ENSURE( false, "StylesBuffer::getColor - unknown color type" ); + } + if( (rColor.mnType != XML_auto) && (nColor != API_RGB_TRANSPARENT) && (rColor.mfTint >= -1.0) && (rColor.mfTint != 0.0) && (rColor.mfTint <= 1.0) ) + nColor = lclTintToColor( nColor, rColor.mfTint ); + return nColor; +} + +FontRef StylesBuffer::getFont( sal_Int32 nFontId ) const +{ + return maFonts.get( nFontId ); +} + +XfRef StylesBuffer::getCellXf( sal_Int32 nXfId ) const +{ + return maCellXfs.get( nXfId ); +} + +XfRef StylesBuffer::getStyleXf( sal_Int32 nXfId ) const +{ + return maStyleXfs.get( nXfId ); +} + +DxfRef StylesBuffer::getDxf( sal_Int32 nDxfId ) const +{ + return maDxfs.get( nDxfId ); +} + +FontRef StylesBuffer::getFontFromCellXf( sal_Int32 nXfId ) const +{ + FontRef xFont; + if( const Xf* pXf = getCellXf( nXfId ).get() ) + xFont = pXf->getFont(); + return xFont; +} + +FontRef StylesBuffer::getDefaultFont() const +{ + FontRef xDefFont; + if( const Xf* pXf = getStyleXf( mnDefStyleXf ).get() ) + xDefFont = pXf->getFont(); + // no font from styles - try first loaded font (e.g. BIFF2) + if( !xDefFont ) + xDefFont = maFonts.get( 0 ); + OSL_ENSURE( xDefFont.get(), "StylesBuffer::getDefaultFont - no default font found" ); + return xDefFont; +} + +const OoxFontData& StylesBuffer::getDefaultFontData() const +{ + FontRef xDefFont = getDefaultFont(); + return xDefFont.get() ? xDefFont->getFontData() : getTheme().getDefaultFontData(); +} + +const OUString& StylesBuffer::createCellStyle( sal_Int32 nXfId ) const +{ + if( CellStyle* pCellStyle = maCellStyles.get( nXfId ).get() ) + return pCellStyle->createCellStyle( nXfId ); + // on error: fallback to default style + return maDefStyleName; +} + +const OUString& StylesBuffer::createDxfStyle( sal_Int32 nDxfId ) const +{ + if( Dxf* pDxf = maDxfs.get( nDxfId ).get() ) + return pDxf->createDxfStyle( nDxfId ); + // on error: fallback to default style + return maDefStyleName; +} + +#if OOX_XLS_USE_DEFAULT_STYLE +#else +const OUString& StylesBuffer::getDefaultStyleName() const +{ + return createCellStyle( mnDefStyleXf ); +} +#endif + +void StylesBuffer::writeFontToPropertySet( PropertySet& rPropSet, sal_Int32 nFontId ) const +{ + if( Font* pFont = maFonts.get( nFontId ).get() ) + pFont->writeToPropertySet( rPropSet, FONT_PROPTYPE_CELL ); +} + +void StylesBuffer::writeNumFmtToPropertySet( PropertySet& rPropSet, sal_Int32 nNumFmtId ) const +{ + maNumFmts.writeToPropertySet( rPropSet, nNumFmtId ); +} + +void StylesBuffer::writeBorderToPropertySet( PropertySet& rPropSet, sal_Int32 nBorderId ) const +{ + if( Border* pBorder = maBorders.get( nBorderId ).get() ) + pBorder->writeToPropertySet( rPropSet ); +} + +void StylesBuffer::writeFillToPropertySet( PropertySet& rPropSet, sal_Int32 nFillId ) const +{ + if( Fill* pFill = maFills.get( nFillId ).get() ) + pFill->writeToPropertySet( rPropSet ); +} + +void StylesBuffer::writeCellXfToPropertySet( PropertySet& rPropSet, sal_Int32 nXfId ) const +{ + if( Xf* pXf = maCellXfs.get( nXfId ).get() ) + pXf->writeToPropertySet( rPropSet ); +} + +void StylesBuffer::writeStyleXfToPropertySet( PropertySet& rPropSet, sal_Int32 nXfId ) const +{ + if( Xf* pXf = maStyleXfs.get( nXfId ).get() ) + pXf->writeToPropertySet( rPropSet ); +} + +void StylesBuffer::insertCellStyle( CellStyleRef xCellStyle ) +{ + if( xCellStyle->getXfId() >= 0 ) + { + maCellStyles[ xCellStyle->getXfId() ] = xCellStyle; + if( xCellStyle->isDefaultStyle() ) + mnDefStyleXf = xCellStyle->getXfId(); + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/stylesfragment.cxx b/oox/source/xls/stylesfragment.cxx new file mode 100644 index 000000000000..0ef9400dcbec --- /dev/null +++ b/oox/source/xls/stylesfragment.cxx @@ -0,0 +1,316 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stylesfragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/stylesfragment.hxx" +#include "oox/helper/attributelist.hxx" + +using ::rtl::OUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +OoxStylesFragment::OoxStylesFragment( + const WorkbookHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ), + mfGradPos( -1.0 ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxStylesFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + sal_Int32 nCurrContext = getCurrentContext(); + switch( nCurrContext ) + { + case XML_ROOT_CONTEXT: + return (nElement == XLS_TOKEN( styleSheet )); + case XLS_TOKEN( styleSheet ): + return (nElement == XLS_TOKEN( colors )) || + (nElement == XLS_TOKEN( fonts )) || + (nElement == XLS_TOKEN( numFmts )) || + (nElement == XLS_TOKEN( borders )) || + (nElement == XLS_TOKEN( fills )) || + (nElement == XLS_TOKEN( cellXfs )) || + (nElement == XLS_TOKEN( cellStyleXfs )) || + (nElement == XLS_TOKEN( dxfs )) || + (nElement == XLS_TOKEN( cellStyles )); + + case XLS_TOKEN( colors ): + return (nElement == XLS_TOKEN( indexedColors )); + case XLS_TOKEN( indexedColors ): + return (nElement == XLS_TOKEN( rgbColor )); + + case XLS_TOKEN( fonts ): + return (nElement == XLS_TOKEN( font )); + case XLS_TOKEN( font ): + return mxFont.get() && Font::isSupportedContext( nElement, nCurrContext ); + + case XLS_TOKEN( numFmts ): + return (nElement == XLS_TOKEN( numFmt )); + + case XLS_TOKEN( borders ): + return (nElement == XLS_TOKEN( border )); + case XLS_TOKEN( border ): + case XLS_TOKEN( left ): + case XLS_TOKEN( right ): + case XLS_TOKEN( top ): + case XLS_TOKEN( bottom ): + case XLS_TOKEN( diagonal ): + return mxBorder.get() && Border::isSupportedContext( nElement, nCurrContext ); + + case XLS_TOKEN( fills ): + return (nElement == XLS_TOKEN( fill )); + case XLS_TOKEN( fill ): + case XLS_TOKEN( patternFill ): + case XLS_TOKEN( gradientFill ): + case XLS_TOKEN( stop ): + return mxFill.get() && Fill::isSupportedContext( nElement, nCurrContext ); + + case XLS_TOKEN( cellStyleXfs ): + case XLS_TOKEN( cellXfs ): + return (nElement == XLS_TOKEN( xf )); + case XLS_TOKEN( xf ): + return mxXf.get() && + ((nElement == XLS_TOKEN( alignment )) || + (nElement == XLS_TOKEN( protection ))); + + case XLS_TOKEN( dxfs ): + return (nElement == XLS_TOKEN( dxf )); + case XLS_TOKEN( dxf ): + return mxDxf.get() && + ((nElement == XLS_TOKEN( font )) || + (nElement == XLS_TOKEN( numFmt )) || + (nElement == XLS_TOKEN( alignment )) || + (nElement == XLS_TOKEN( protection )) || + (nElement == XLS_TOKEN( border )) || + (nElement == XLS_TOKEN( fill ))); + + case XLS_TOKEN( cellStyles ): + return (nElement == XLS_TOKEN( cellStyle )); + } + return false; +} + +void OoxStylesFragment::onStartElement( const AttributeList& rAttribs ) +{ + sal_Int32 nCurrContext = getCurrentContext(); + sal_Int32 nPrevContext = getPreviousContext(); + + switch( nCurrContext ) + { + case XLS_TOKEN( color ): + switch( nPrevContext ) + { + case XLS_TOKEN( font ): + OSL_ENSURE( mxFont.get(), "OoxStylesFragment::onStartElement - missing font object" ); + mxFont->importAttribs( nCurrContext, rAttribs ); + break; + case XLS_TOKEN( stop ): + OSL_ENSURE( mxFill.get(), "OoxStylesFragment::onStartElement - missing fill object" ); + mxFill->importColor( rAttribs, mfGradPos ); + break; + default: + OSL_ENSURE( mxBorder.get(), "OoxStylesFragment::onStartElement - missing border object" ); + mxBorder->importColor( nPrevContext, rAttribs ); + } + break; + case XLS_TOKEN( rgbColor ): + getStyles().importPaletteColor( rAttribs ); + break; + + case XLS_TOKEN( font ): + mxFont = mxDxf.get() ? mxDxf->importFont( rAttribs ) : getStyles().importFont( rAttribs ); + break; + + case XLS_TOKEN( numFmt ): + if( mxDxf.get() ) + mxDxf->importNumFmt( rAttribs ); + else + getStyles().importNumFmt( rAttribs ); + break; + + case XLS_TOKEN( alignment ): + OSL_ENSURE( mxXf.get() || mxDxf.get(), "OoxStylesFragment::onStartElement - missing formatting object" ); + if( mxXf.get() ) + mxXf->importAlignment( rAttribs ); +#if 0 + else if( mxDxf.get() ) + mxDxf->importAlignment( rAttribs ); +#endif + break; + + case XLS_TOKEN( protection ): + OSL_ENSURE( mxXf.get() || mxDxf.get(), "OoxStylesFragment::onStartElement - missing formatting object" ); + if( mxXf.get() ) + mxXf->importProtection( rAttribs ); +#if 0 + else if( mxDxf.get() ) + mxDxf->importProtection( rAttribs ); +#endif + break; + + case XLS_TOKEN( border ): + mxBorder = mxDxf.get() ? mxDxf->importBorder( rAttribs ) : getStyles().importBorder( rAttribs ); + break; + + case XLS_TOKEN( fill ): + mxFill = mxDxf.get() ? mxDxf->importFill( rAttribs ) : getStyles().importFill( rAttribs ); + break; + case XLS_TOKEN( patternFill ): + OSL_ENSURE( mxFill.get(), "OoxStylesFragment::onStartElement - missing fill object" ); + mxFill->importPatternFill( rAttribs ); + break; + case XLS_TOKEN( fgColor ): + OSL_ENSURE( mxFill.get(), "OoxStylesFragment::onStartElement - missing fill object" ); + mxFill->importFgColor( rAttribs ); + break; + case XLS_TOKEN( bgColor ): + OSL_ENSURE( mxFill.get(), "OoxStylesFragment::onStartElement - missing fill object" ); + mxFill->importBgColor( rAttribs ); + break; + case XLS_TOKEN( gradientFill ): + OSL_ENSURE( mxFill.get(), "OoxStylesFragment::onStartElement - missing fill object" ); + mxFill->importGradientFill( rAttribs ); + break; + case XLS_TOKEN( stop ): + mfGradPos = rAttribs.getDouble( XML_position, -1.0 ); + break; + + case XLS_TOKEN( xf ): + mxXf = getStyles().importXf( nPrevContext, rAttribs ); + break; + case XLS_TOKEN( dxf ): + mxDxf = getStyles().importDxf( rAttribs ); + break; + + case XLS_TOKEN( cellStyle ): + getStyles().importCellStyle( rAttribs ); + break; + + default: switch( nPrevContext ) + { + case XLS_TOKEN( font ): + OSL_ENSURE( mxFont.get(), "OoxStylesFragment::onStartElement - missing font object" ); + mxFont->importAttribs( nCurrContext, rAttribs ); + break; + case XLS_TOKEN( border ): + OSL_ENSURE( mxBorder.get(), "OoxStylesFragment::onStartElement - missing border object" ); + mxBorder->importStyle( nCurrContext, rAttribs ); + break; + } + } +} + +void OoxStylesFragment::onEndElement( const OUString& /*rChars*/ ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( font ): mxFont.reset(); break; + case XLS_TOKEN( border ): mxBorder.reset(); break; + case XLS_TOKEN( fill ): mxFill.reset(); break; + case XLS_TOKEN( xf ): mxXf.reset(); break; + case XLS_TOKEN( dxf ): mxDxf.reset(); break; + } +} + +bool OoxStylesFragment::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nRecId == OOBIN_ID_STYLESHEET); + case OOBIN_ID_STYLESHEET: + return (nRecId == OOBIN_ID_COLORS) || + (nRecId == OOBIN_ID_FONTS) || + (nRecId == OOBIN_ID_NUMFMTS) || + (nRecId == OOBIN_ID_BORDERS) || + (nRecId == OOBIN_ID_FILLS) || + (nRecId == OOBIN_ID_CELLSTYLEXFS) || + (nRecId == OOBIN_ID_CELLXFS) || + (nRecId == OOBIN_ID_DXFS) || + (nRecId == OOBIN_ID_CELLSTYLES); + case OOBIN_ID_COLORS: + return (nRecId == OOBIN_ID_INDEXEDCOLORS); + case OOBIN_ID_INDEXEDCOLORS: + return (nRecId == OOBIN_ID_RGBCOLOR); + case OOBIN_ID_FONTS: + return (nRecId == OOBIN_ID_FONT); + case OOBIN_ID_NUMFMTS: + return (nRecId == OOBIN_ID_NUMFMT); + case OOBIN_ID_BORDERS: + return (nRecId == OOBIN_ID_BORDER); + case OOBIN_ID_FILLS: + return (nRecId == OOBIN_ID_FILL); + case OOBIN_ID_CELLSTYLEXFS: + case OOBIN_ID_CELLXFS: + return (nRecId == OOBIN_ID_XF); + case OOBIN_ID_DXFS: + return (nRecId == OOBIN_ID_DXF); + case OOBIN_ID_CELLSTYLES: + return (nRecId == OOBIN_ID_CELLSTYLE); + } + return false; +} + +void OoxStylesFragment::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_RGBCOLOR: getStyles().importPaletteColor( rStrm ); break; + case OOBIN_ID_FONT: getStyles().importFont( rStrm ); break; + case OOBIN_ID_NUMFMT: getStyles().importNumFmt( rStrm ); break; + case OOBIN_ID_BORDER: getStyles().importBorder( rStrm ); break; + case OOBIN_ID_FILL: getStyles().importFill( rStrm ); break; + case OOBIN_ID_XF: getStyles().importXf( getPreviousContext(), rStrm ); break; + case OOBIN_ID_DXF: getStyles().importDxf( rStrm ); break; + case OOBIN_ID_CELLSTYLE: getStyles().importCellStyle( rStrm ); break; + } +} + +// oox.xls.OoxFragmentHandler interface --------------------------------------- + +void OoxStylesFragment::finalizeImport() +{ + getStyles().finalizeImport(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/stylespropertyhelper.cxx b/oox/source/xls/stylespropertyhelper.cxx new file mode 100644 index 000000000000..91a8099de426 --- /dev/null +++ b/oox/source/xls/stylespropertyhelper.cxx @@ -0,0 +1,386 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stylespropertyhelper.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/stylespropertyhelper.hxx" +#include <com/sun/star/awt/FontFamily.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/FontPitch.hpp> +#include <com/sun/star/awt/FontType.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include "oox/helper/propertyset.hxx" +#include "oox/xls/stylesbuffer.hxx" + +using ::rtl::OUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +ApiFontUsedFlags::ApiFontUsedFlags( bool bAllUsed ) : + mbNameUsed( bAllUsed ), + mbColorUsed( bAllUsed ), + mbSchemeUsed( bAllUsed ), + mbHeightUsed( bAllUsed ), + mbUnderlineUsed( bAllUsed ), + mbEscapementUsed( bAllUsed ), + mbWeightUsed( bAllUsed ), + mbPostureUsed( bAllUsed ), + mbStrikeoutUsed( bAllUsed ), + mbOutlineUsed( bAllUsed ), + mbShadowUsed( bAllUsed ) +{ +} + +// ---------------------------------------------------------------------------- + +ApiFontData::ApiFontData() : + maDesc( + CREATE_OUSTRING( "Calibri" ), + 220, // height 11 points + 0, + OUString(), + ::com::sun::star::awt::FontFamily::DONTKNOW, + RTL_TEXTENCODING_DONTKNOW, + ::com::sun::star::awt::FontPitch::DONTKNOW, + 100.0, + ::com::sun::star::awt::FontWeight::NORMAL, + ::com::sun::star::awt::FontSlant_NONE, + ::com::sun::star::awt::FontUnderline::NONE, + ::com::sun::star::awt::FontStrikeout::NONE, + 0.0, + sal_False, + sal_False, + ::com::sun::star::awt::FontType::DONTKNOW ), + mnColor( API_RGB_TRANSPARENT ), + mnEscapement( API_ESCAPE_NONE ), + mnEscapeHeight( API_ESCAPEHEIGHT_NONE ), + mbOutline( false ), + mbShadow( false ), + mbHasWstrn( true ), + mbHasAsian( false ) +{ +} + +// ============================================================================ + +ApiNumFmtData::ApiNumFmtData() : + mnIndex( 0 ) +{ +} + +// ============================================================================ + +ApiAlignmentData::ApiAlignmentData() : + meHorJustify( ::com::sun::star::table::CellHoriJustify_STANDARD ), + meVerJustify( ::com::sun::star::table::CellVertJustify_STANDARD ), + meOrientation( ::com::sun::star::table::CellOrientation_STANDARD ), + mnRotation( 0 ), + mnWritingMode( ::com::sun::star::text::WritingMode2::PAGE ), + mnIndent( 0 ), + mbWrapText( false ), + mbShrink( false ) +{ +} + +bool operator==( const ApiAlignmentData& rLeft, const ApiAlignmentData& rRight ) +{ + return + (rLeft.meHorJustify == rRight.meHorJustify) && + (rLeft.meVerJustify == rRight.meVerJustify) && + (rLeft.meOrientation == rRight.meOrientation) && + (rLeft.mnRotation == rRight.mnRotation) && + (rLeft.mnWritingMode == rRight.mnWritingMode) && + (rLeft.mnIndent == rRight.mnIndent) && + (rLeft.mbWrapText == rRight.mbWrapText) && + (rLeft.mbShrink == rRight.mbShrink); +} + +// ============================================================================ + +ApiProtectionData::ApiProtectionData() : + maCellProt( sal_True, sal_False, sal_False, sal_False ) +{ +} + +bool operator==( const ApiProtectionData& rLeft, const ApiProtectionData& rRight ) +{ + return + (rLeft.maCellProt.IsLocked == rRight.maCellProt.IsLocked) && + (rLeft.maCellProt.IsFormulaHidden == rRight.maCellProt.IsFormulaHidden) && + (rLeft.maCellProt.IsHidden == rRight.maCellProt.IsHidden) && + (rLeft.maCellProt.IsPrintHidden == rRight.maCellProt.IsPrintHidden); +} + +// ============================================================================ + +ApiBorderData::ApiBorderData() : + mbBorderUsed( false ), + mbDiagUsed( false ) +{ +} + +// ============================================================================ + +ApiSolidFillData::ApiSolidFillData() : + mnColor( API_RGB_TRANSPARENT ), + mbTransparent( true ), + mbUsed( false ) +{ +} + +// ============================================================================ + +namespace { + +/** Property names for Western font name settings. */ +const sal_Char* const sppcWstrnFontNameNames[] = +{ + "CharFontName", + "CharFontFamily", + "CharFontCharSet", + 0 +}; + +/** Property names for Asian font name settings. */ +const sal_Char* const sppcAsianFontNameNames[] = +{ + "CharFontNameAsian", + "CharFontFamilyAsian", + "CharFontCharSetAsian", + 0 +}; + +/** Property names for Complex font name settings. */ +const sal_Char* const sppcCmplxFontNameNames[] = +{ + "CharFontNameComplex", + "CharFontFamilyComplex", + "CharFontCharSetComplex", + 0 +}; + +/** Property names for font height settings. */ +const sal_Char* const sppcFontHeightNames[] = +{ + "CharHeight", + "CharHeightAsian", + "CharHeightComplex", + 0 +}; + +/** Property names for font weight settings. */ +const sal_Char* const sppcFontWeightNames[] = +{ + "CharWeight", + "CharWeightAsian", + "CharWeightComplex", + 0 +}; + +/** Property names for font posture settings. */ +const sal_Char* const sppcFontPostureNames[] = +{ + "CharPosture", + "CharPostureAsian", + "CharPostureComplex", + 0 +}; + +/** Property names for font escapement settings. */ +const sal_Char* const sppcFontEscapeNames[] = +{ + "CharEscapement", + "CharEscapementHeight", + 0 +}; + +/** Property names for alignment. */ +const sal_Char* const sppcAlignmentNames[] = +{ + "HoriJustify", + "VertJustify", + "WritingMode", + "RotateAngle", + "RotateReference", + "Orientation", + "ParaIndent", + "IsTextWrapped", + "ShrinkToFit", + 0 +}; + +/** Property names for diagonal cell borders. */ +const sal_Char* const sppcDiagBorderNames[] = +{ + "DiagonalTLBR", + "DiagonalBLTR", + 0 +}; + +/** Property names for cell fill. */ +const sal_Char* const sppcSolidFillNames[] = +{ + "CellBackColor", + "IsCellBackgroundTransparent", + 0 +}; + +} // namespace + +// ---------------------------------------------------------------------------- + +StylesPropertyHelper::StylesPropertyHelper( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maWstrnFontNameProps( sppcWstrnFontNameNames ), + maAsianFontNameProps( sppcAsianFontNameNames ), + maCmplxFontNameProps( sppcCmplxFontNameNames ), + maFontHeightProps( sppcFontHeightNames ), + maFontWeightProps( sppcFontWeightNames ), + maFontPostureProps( sppcFontPostureNames ), + maFontEscapeProps( sppcFontEscapeNames ), + maAlignProps( sppcAlignmentNames ), + maDiagBorderProps( sppcDiagBorderNames ), + maSolidFillProps( sppcSolidFillNames ), + maCharColorProp( CREATE_OUSTRING( "CharColor" ) ), + maCharUnderlineProp( CREATE_OUSTRING( "CharUnderline" ) ), + maCharStrikeoutProp( CREATE_OUSTRING( "CharStrikeout" ) ), + maCharContouredProp( CREATE_OUSTRING( "CharContoured" ) ), + maCharShadowedProp( CREATE_OUSTRING( "CharShadowed" ) ), + maNumFmtProp( CREATE_OUSTRING( "NumberFormat" ) ), + maCellProtProp( CREATE_OUSTRING( "CellProtection" ) ), + maBorderProp( CREATE_OUSTRING( "TableBorder" ) ) +{ +} + +void StylesPropertyHelper::writeFontProperties( PropertySet& rPropSet, + const ApiFontData& rFontData, const ApiFontUsedFlags& rUsedFlags, FontPropertyType ePropType ) +{ + // font name properties + if( rUsedFlags.mbNameUsed ) + { + if( rFontData.mbHasWstrn ) + maWstrnFontNameProps << rFontData.maDesc.Name << rFontData.maDesc.Family << rFontData.maDesc.CharSet >> rPropSet; + if( rFontData.mbHasAsian ) + maAsianFontNameProps << rFontData.maDesc.Name << rFontData.maDesc.Family << rFontData.maDesc.CharSet >> rPropSet; + if( rFontData.mbHasCmplx ) + maCmplxFontNameProps << rFontData.maDesc.Name << rFontData.maDesc.Family << rFontData.maDesc.CharSet >> rPropSet; + } + // font height + if( rUsedFlags.mbHeightUsed ) + { + float fHeight = static_cast< float >( rFontData.maDesc.Height / 20.0 ); // twips to points + maFontHeightProps << fHeight << fHeight << fHeight >> rPropSet; + } + // font weight + if( rUsedFlags.mbWeightUsed ) + { + float fWeight = rFontData.maDesc.Weight; + maFontWeightProps << fWeight << fWeight << fWeight >> rPropSet; + } + // font posture + if( rUsedFlags.mbPostureUsed ) + maFontPostureProps << rFontData.maDesc.Slant << rFontData.maDesc.Slant << rFontData.maDesc.Slant >> rPropSet; + // character color + if( rUsedFlags.mbColorUsed ) + rPropSet.setProperty( maCharColorProp, rFontData.mnColor ); + // underline style + if( rUsedFlags.mbUnderlineUsed ) + rPropSet.setProperty( maCharUnderlineProp, rFontData.maDesc.Underline ); + // strike out style + if( rUsedFlags.mbStrikeoutUsed ) + rPropSet.setProperty( maCharStrikeoutProp, rFontData.maDesc.Strikeout ); + // outline style + if( rUsedFlags.mbOutlineUsed ) + rPropSet.setProperty( maCharContouredProp, rFontData.mbOutline ); + // shadow style + if( rUsedFlags.mbShadowUsed ) + rPropSet.setProperty( maCharShadowedProp, rFontData.mbShadow ); + // escapement + if( rUsedFlags.mbEscapementUsed && (ePropType == FONT_PROPTYPE_RICHTEXT) ) + maFontEscapeProps << rFontData.mnEscapement << rFontData.mnEscapeHeight >> rPropSet; +} + +void StylesPropertyHelper::writeNumFmtProperties( + PropertySet& rPropSet, const ApiNumFmtData& rNumFmtData ) +{ + rPropSet.setProperty( maNumFmtProp, rNumFmtData.mnIndex ); +} + +void StylesPropertyHelper::writeAlignmentProperties( + PropertySet& rPropSet, const ApiAlignmentData& rAlignData ) +{ + maAlignProps + << rAlignData.meHorJustify + << rAlignData.meVerJustify + << rAlignData.mnWritingMode + << rAlignData.mnRotation + << ::com::sun::star::table::CellVertJustify_STANDARD // rotation reference + << rAlignData.meOrientation + << rAlignData.mnIndent + << rAlignData.mbWrapText + << rAlignData.mbShrink + >> rPropSet; +} + +void StylesPropertyHelper::writeProtectionProperties( + PropertySet& rPropSet, const ApiProtectionData& rProtData ) +{ + rPropSet.setProperty( maCellProtProp, rProtData.maCellProt ); +} + +void StylesPropertyHelper::writeBorderProperties( + PropertySet& rPropSet, const ApiBorderData& rBorderData ) +{ + if( rBorderData.mbBorderUsed ) + rPropSet.setProperty( maBorderProp, rBorderData.maBorder ); + if( rBorderData.mbDiagUsed ) + maDiagBorderProps << rBorderData.maTLtoBR << rBorderData.maBLtoTR >> rPropSet; +} + +void StylesPropertyHelper::writeSolidFillProperties( + PropertySet& rPropSet, const ApiSolidFillData& rFillData ) +{ + if( rFillData.mbUsed ) + maSolidFillProps << rFillData.mnColor << rFillData.mbTransparent >> rPropSet; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/tablebuffer.cxx b/oox/source/xls/tablebuffer.cxx new file mode 100644 index 000000000000..5865422f62e5 --- /dev/null +++ b/oox/source/xls/tablebuffer.cxx @@ -0,0 +1,172 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tablebuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/tablebuffer.hxx" +#include <com/sun/star/sheet/XDatabaseRanges.hpp> +#include <com/sun/star/sheet/XDatabaseRange.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/containerhelper.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/addressconverter.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::sheet::XDatabaseRanges; +using ::com::sun::star::sheet::XDatabaseRange; + +namespace oox { +namespace xls { + +// ============================================================================ + +OoxTableData::OoxTableData() : + mnId( -1 ), + mnType( XML_worksheet ), + mnHeaderRows( 1 ), + mnTotalsRows( 0 ) +{ +} + +// ============================================================================ + +Table::Table( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mnTokenIndex( -1 ) +{ +} + +void Table::importTable( const AttributeList& rAttribs, sal_Int16 nSheet ) +{ + if( getAddressConverter().convertToCellRange( maOoxData.maRange, rAttribs.getString( XML_ref ), nSheet, true ) ) + { + maOoxData.maProgName = rAttribs.getString( XML_name ); + maOoxData.maDisplayName = rAttribs.getString( XML_displayName ); + maOoxData.mnId = rAttribs.getInteger( XML_id, -1 ); + maOoxData.mnType = rAttribs.getToken( XML_tableType, XML_worksheet ); + maOoxData.mnHeaderRows = rAttribs.getInteger( XML_headerRowCount, 1 ); + maOoxData.mnTotalsRows = rAttribs.getInteger( XML_totalsRowCount, 0 ); + } +} + +void Table::importTable( RecordInputStream& rStrm, sal_Int16 nSheet ) +{ + BinRange aBinRange; + rStrm >> aBinRange; + if( getAddressConverter().convertToCellRange( maOoxData.maRange, aBinRange, nSheet, true ) ) + { + sal_Int32 nType; + rStrm >> nType >> maOoxData.mnId >> maOoxData.mnHeaderRows >> maOoxData.mnTotalsRows; + rStrm.skip( 32 ); + rStrm >> maOoxData.maProgName >> maOoxData.maDisplayName; + + static const sal_Int32 spnTypes[] = { XML_worksheet, XML_TOKEN_INVALID, XML_TOKEN_INVALID, XML_queryTable }; + maOoxData.mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID ); + } +} + +void Table::finalizeImport() +{ + if( maOoxData.maDisplayName.getLength() > 0 ) try + { + // find an unused name + Reference< XDatabaseRanges > xDatabaseRanges = getDatabaseRanges(); + Reference< XNameAccess > xNameAccess( xDatabaseRanges, UNO_QUERY_THROW ); + OUString aName = ContainerHelper::getUnusedName( xNameAccess, maOoxData.maDisplayName, '_' ); + xDatabaseRanges->addNewByName( aName, maOoxData.maRange ); + Reference< XDatabaseRange > xDatabaseRange( xDatabaseRanges->getByName( aName ), UNO_QUERY_THROW ); + PropertySet aPropSet( xDatabaseRange ); + if( !aPropSet.getProperty( mnTokenIndex, CREATE_OUSTRING( "TokenIndex" ) ) ) + mnTokenIndex = -1; + } + catch( Exception& ) + { + OSL_ENSURE( false, "Table::finalizeImport - cannot create database range" ); + } +} + +// ============================================================================ + +TableBuffer::TableBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +TableRef TableBuffer::importTable( const AttributeList& rAttribs, sal_Int16 nSheet ) +{ + TableRef xTable( new Table( *this ) ); + xTable->importTable( rAttribs, nSheet ); + insertTable( xTable ); + return xTable; +} + +TableRef TableBuffer::importTable( RecordInputStream& rStrm, sal_Int16 nSheet ) +{ + TableRef xTable( new Table( *this ) ); + xTable->importTable( rStrm, nSheet ); + insertTable( xTable ); + return xTable; +} + +void TableBuffer::finalizeImport() +{ + maTables.forEachMem( &Table::finalizeImport ); +} + +TableRef TableBuffer::getTable( sal_Int32 nTableId ) const +{ + return maTables.get( nTableId ); +} + +// private -------------------------------------------------------------------- + +void TableBuffer::insertTable( TableRef xTable ) +{ + sal_Int32 nTableId = xTable->getTableId(); + if( nTableId > 0 ) + { + OSL_ENSURE( maTables.find( nTableId ) == maTables.end(), "TableBuffer::insertTable - multiple table identifier" ); + maTables[ nTableId ] = xTable; + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/tablefragment.cxx b/oox/source/xls/tablefragment.cxx new file mode 100644 index 000000000000..53097159a05d --- /dev/null +++ b/oox/source/xls/tablefragment.cxx @@ -0,0 +1,92 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tablefragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/tablefragment.hxx" + +using ::rtl::OUString; + +namespace oox { +namespace xls { + +// ============================================================================ + +OoxTableFragment::OoxTableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorksheetFragmentBase( rHelper, rFragmentPath ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxTableFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nElement == XLS_TOKEN( table )); + } + return false; +} + +void OoxTableFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( table ): mxTable = getTables().importTable( rAttribs, getSheetIndex() ); break; + } +} + +bool OoxTableFragment::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nRecId == OOBIN_ID_TABLE); + } + return false; +} + +void OoxTableFragment::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_TABLE: mxTable = getTables().importTable( rStrm, getSheetIndex() ); break; + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/themebuffer.cxx b/oox/source/xls/themebuffer.cxx new file mode 100644 index 000000000000..1a930aa26845 --- /dev/null +++ b/oox/source/xls/themebuffer.cxx @@ -0,0 +1,170 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: themebuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/themebuffer.hxx" +#include "oox/xls/stylesbuffer.hxx" +#include "oox/xls/stylespropertyhelper.hxx" + +using ::oox::drawingml::ClrScheme; +using ::oox::drawingml::Theme; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +/** Specifies default theme fonts for a specific locale. */ +struct BuiltinThemeFont +{ + const sal_Char* mpcLocale; /// The locale for this font setting. + const sal_Char* mpcHeadFont; /// Default heading font. + const sal_Char* mpcBodyFont; /// Default body font. +}; + +#define FONT_JA "\357\274\255\357\274\263 \357\274\260\343\202\264\343\202\267\343\203\203\343\202\257" +#define FONT_KO "\353\247\221\354\235\200 \352\263\240\353\224\225" +#define FONT_CS "\345\256\213\344\275\223" +#define FONT_CT "\346\226\260\347\264\260\346\230\216\351\253\224" + +static const BuiltinThemeFont spBuiltinThemeFonts[] = +{ // locale headings font body font + { "*", "Cambria", "Calibri" }, // Default + { "ar", "Times New Roman", "Arial" }, // Arabic + { "bn", "Vrinda", "Vrinda" }, // Bengali + { "div", "MV Boli", "MV Boli" }, // Divehi + { "fa", "Times New Roman", "Arial" }, // Farsi + { "gu", "Shruti", "Shruti" }, // Gujarati + { "he", "Times New Roman", "Arial" }, // Hebrew + { "hi", "Mangal", "Mangal" }, // Hindi + { "ja", FONT_JA, FONT_JA }, // Japanese + { "kn", "Tunga", "Tunga" }, // Kannada + { "ko", FONT_KO, FONT_KO }, // Korean + { "kok", "Mangal", "Mangal" }, // Konkani + { "ml", "Kartika", "Kartika" }, // Malayalam + { "mr", "Mangal", "Mangal" }, // Marathi + { "pa", "Raavi", "Raavi" }, // Punjabi + { "sa", "Mangal", "Mangal" }, // Sanskrit + { "syr", "Estrangelo Edessa", "Estrangelo Edessa" }, // Syriac + { "ta", "Latha", "Latha" }, // Tamil + { "te", "Gautami", "Gautami" }, // Telugu + { "th", "Tahoma", "Tahoma" }, // Thai + { "ur", "Times New Roman", "Arial" }, // Urdu + { "vi", "Times New Roman", "Arial" }, // Vietnamese + { "zh", FONT_CS, FONT_CS }, // Chinese, Simplified + { "zh-HK", FONT_CT, FONT_CT }, // Chinese, Hong Kong + { "zh-MO", FONT_CT, FONT_CT }, // Chinese, Macau + { "zh-TW", FONT_CT, FONT_CT } // Chinese, Taiwan +}; + +} // namespace + +// ---------------------------------------------------------------------------- + +ThemeBuffer::ThemeBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mxDefFontData( new OoxFontData ) +{ + switch( getFilterType() ) + { + case FILTER_OOX: + //! TODO: locale dependent font name + mxDefFontData->maName = CREATE_OUSTRING( "Cambria" ); + mxDefFontData->mfHeight = 11.0; + break; + case FILTER_BIFF: + //! TODO: BIFF dependent font name + mxDefFontData->maName = CREATE_OUSTRING( "Arial" ); + mxDefFontData->mfHeight = 10.0; + break; + case FILTER_UNKNOWN: break; + } +} + +ThemeBuffer::~ThemeBuffer() +{ +} + +Theme& ThemeBuffer::getCoreTheme() const +{ + if( !mxTheme ) + mxTheme.reset( new Theme ); + return *mxTheme; +} + +sal_Int32 ThemeBuffer::getColorByToken( sal_Int32 nToken ) const +{ + sal_Int32 nColor = 0; + if( const ClrScheme* pClrScheme = getCoreTheme().getClrScheme().get() ) + if( pClrScheme->getColor( nToken, nColor ) ) + return nColor; + return API_RGB_TRANSPARENT; +} + +sal_Int32 ThemeBuffer::getColorByIndex( sal_Int32 nIndex ) const +{ + static const sal_Int32 spnColorTokens[] = { + XML_lt1, XML_dk1, XML_lt2, XML_dk2, XML_accent1, XML_accent2, + XML_accent3, XML_accent4, XML_accent5, XML_accent6, XML_hlink, XML_folHlink }; + + sal_Int32 nColor = 0; + if( const ClrScheme* pClrScheme = getCoreTheme().getClrScheme().get() ) + if( pClrScheme->getColor( STATIC_ARRAY_SELECT( spnColorTokens, nIndex, XML_TOKEN_INVALID ), nColor ) ) + return nColor; + return API_RGB_TRANSPARENT; +} + +sal_Int32 ThemeBuffer::getSystemColor( sal_Int32 nElement, sal_Int32 nDefaultColor ) +{ + sal_Int32 nColor = 0; + return ClrScheme::getSystemColor( nColor, nElement ) ? nColor : nDefaultColor; +} + +sal_Int32 ThemeBuffer::getSystemWindowColor() +{ + return getSystemColor( XML_window, 0xFFFFFF ); +} + +sal_Int32 ThemeBuffer::getSystemWindowTextColor() +{ + return getSystemColor( XML_windowText, 0x000000 ); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/unitconverter.cxx b/oox/source/xls/unitconverter.cxx new file mode 100644 index 000000000000..66f92142413b --- /dev/null +++ b/oox/source/xls/unitconverter.cxx @@ -0,0 +1,209 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: unitconverter.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/unitconverter.hxx" +#include <com/sun/star/awt/FontDescriptor.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/DeviceInfo.hpp> +#include <com/sun/star/awt/XFont.hpp> +#include "oox/xls/stylesbuffer.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::awt::FontDescriptor; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::awt::DeviceInfo; +using ::com::sun::star::awt::XFont; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const double MM100_PER_INCH = 2540.0; +const double INCH_PER_MM100 = 1.0 / MM100_PER_INCH; + +const double POINT_PER_INCH = 72.0; +const double INCH_PER_POINT = 1.0 / POINT_PER_INCH; + +const double MM100_PER_POINT = MM100_PER_INCH * INCH_PER_POINT; +const double POINT_PER_MM100 = 1.0 / MM100_PER_POINT; + +const double TWIP_PER_POINT = 20.0; +const double POINT_PER_TWIP = 1.0 / TWIP_PER_POINT; + +const double MM100_PER_TWIP = MM100_PER_POINT * POINT_PER_TWIP; +const double TWIP_PER_MM100 = 1.0 / MM100_PER_TWIP; + +} // namespace + +// ---------------------------------------------------------------------------- + +UnitConverter::UnitConverter( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + mfPixelPerMm100X( 0.08 ), + mfPixelPerMm100Y( 0.08 ), + mnDigitWidth( 200 ), + mnSpaceWidth( 100 ) +{ + // map error code names to BIFF error codes + maErrorCodes[ CREATE_OUSTRING( "#NULL!" ) ] = BIFF_ERR_NULL; + maErrorCodes[ CREATE_OUSTRING( "#DIV/0!" ) ] = BIFF_ERR_DIV0; + maErrorCodes[ CREATE_OUSTRING( "#VALUE!" ) ] = BIFF_ERR_VALUE; + maErrorCodes[ CREATE_OUSTRING( "#REF!" ) ] = BIFF_ERR_REF; + maErrorCodes[ CREATE_OUSTRING( "#NAME?" ) ] = BIFF_ERR_NAME; + maErrorCodes[ CREATE_OUSTRING( "#NUM!" ) ] = BIFF_ERR_NUM; + maErrorCodes[ CREATE_OUSTRING( "#NA" ) ] = BIFF_ERR_NA; +} + +void UnitConverter::finalizeImport() +{ + Reference< XDevice > xDevice = getReferenceDevice(); + if( xDevice.is() ) + { + // get pixel metric first, needed to get character widths below + DeviceInfo aInfo = xDevice->getInfo(); + mfPixelPerMm100X = aInfo.PixelPerMeterX / 100000.0; + mfPixelPerMm100Y = aInfo.PixelPerMeterY / 100000.0; + + // get character widths from default font + if( const Font* pDefFont = getStyles().getDefaultFont().get() ) + { + // XDevice expects pixels in font descriptor, but font contains twips + FontDescriptor aDesc = pDefFont->getFontDescriptor(); + aDesc.Height = static_cast< sal_Int16 >( calcPixelsXFromMm100( calcMm100FromTwips( aDesc.Height ) ) ); + Reference< XFont > xFont = xDevice->getFont( aDesc ); + if( xFont.is() ) + { + // get maximum width of all digits + sal_Int32 nDigitWidth = 0; + for( sal_Unicode cChar = '0'; cChar <= '9'; ++cChar ) + nDigitWidth = ::std::max( nDigitWidth, calcMm100FromPixelsX( xFont->getCharWidth( cChar ) ) ); + if( nDigitWidth > 0 ) + mnDigitWidth = nDigitWidth; + // get width of space character + sal_Int32 nSpaceWidth = calcMm100FromPixelsX( xFont->getCharWidth( ' ' ) ); + if( nSpaceWidth > 0 ) + mnSpaceWidth = nSpaceWidth; + } + } + } +} + +// conversion ----------------------------------------------------------------- + +sal_Int32 UnitConverter::calcMm100FromInches( double fInches ) const +{ + return static_cast< sal_Int32 >( fInches * MM100_PER_INCH ); +} + +sal_Int32 UnitConverter::calcMm100FromPoints( double fPoints ) const +{ + return static_cast< sal_Int32 >( fPoints * MM100_PER_POINT + 0.5 ); +} + +sal_Int32 UnitConverter::calcMm100FromTwips( double fTwips ) const +{ + return static_cast< sal_Int32 >( fTwips * MM100_PER_TWIP + 0.5 ); +} + +sal_Int32 UnitConverter::calcMm100FromPixelsX( double fPixels ) const +{ + return static_cast< sal_Int32 >( fPixels / mfPixelPerMm100X + 0.5 ); +} + +sal_Int32 UnitConverter::calcMm100FromPixelsY( double fPixels ) const +{ + return static_cast< sal_Int32 >( fPixels / mfPixelPerMm100Y + 0.5 ); +} + +sal_Int32 UnitConverter::calcMm100FromDigits( double fChars ) const +{ + return static_cast< sal_Int32 >( fChars * mnDigitWidth + 0.5 ); +} + +sal_Int32 UnitConverter::calcMm100FromSpaces( double fSpaces ) const +{ + return static_cast< sal_Int32 >( fSpaces * mnSpaceWidth + 0.5 ); +} + +double UnitConverter::calcInchesFromMm100( sal_Int32 nMm100 ) const +{ + return nMm100 * INCH_PER_MM100; +} + +double UnitConverter::calcPointsFromMm100( sal_Int32 nMm100 ) const +{ + return nMm100 * POINT_PER_MM100; +} + +double UnitConverter::calcTwipsFromMm100( sal_Int32 nMm100 ) const +{ + return nMm100 * TWIP_PER_MM100; +} + +double UnitConverter::calcPixelsXFromMm100( sal_Int32 nMm100 ) const +{ + return nMm100 * mfPixelPerMm100X; +} + +double UnitConverter::calcPixelsYFromMm100( sal_Int32 nMm100 ) const +{ + return nMm100 * mfPixelPerMm100Y; +} + +double UnitConverter::calcDigitsFromMm100( sal_Int32 nMm100 ) const +{ + return static_cast< double >( nMm100 ) / mnDigitWidth; +} + +double UnitConverter::calcSpacesFromMm100( sal_Int32 nMm100 ) const +{ + return static_cast< double >( nMm100 ) / mnSpaceWidth; +} + +sal_uInt8 UnitConverter::calcBiffErrorCode( const OUString& rErrorCode ) const +{ + ErrorCodeMap::const_iterator aIt = maErrorCodes.find( rErrorCode ); + return (aIt == maErrorCodes.end()) ? BIFF_ERR_NA : aIt->second; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/validationpropertyhelper.cxx b/oox/source/xls/validationpropertyhelper.cxx new file mode 100644 index 000000000000..8784fad81cb0 --- /dev/null +++ b/oox/source/xls/validationpropertyhelper.cxx @@ -0,0 +1,174 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: validationpropertyhelper.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/validationpropertyhelper.hxx" +#include <com/sun/star/sheet/ValidationType.hpp> +#include <com/sun/star/sheet/ValidationAlertStyle.hpp> +#include <com/sun/star/sheet/TableValidationVisibility.hpp> +#include <com/sun/star/sheet/XSheetCondition.hpp> +#include <com/sun/star/sheet/XMultiFormulaTokens.hpp> +#include "oox/helper/propertyset.hxx" +#include "oox/xls/ooxtokens.hxx" +#include "oox/xls/worksheethelper.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::sheet::ValidationType; +using ::com::sun::star::sheet::ValidationAlertStyle; +using ::com::sun::star::sheet::ConditionOperator; +using ::com::sun::star::sheet::XSheetCondition; +using ::com::sun::star::sheet::XMultiFormulaTokens; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_Char* const sppcPropNames[] = +{ + "Type", + "ShowInputMessage", + "InputTitle", + "InputMessage", + "ShowErrorMessage", + "ErrorTitle", + "ErrorMessage", + "ErrorAlertStyle", + "ShowList", + "IgnoreBlankCells", + 0 +}; + +} // namespace + +// ---------------------------------------------------------------------------- + +ValidationPropertyHelper::ValidationPropertyHelper( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maValProps( sppcPropNames ), + maValidationProp( CREATE_OUSTRING( "Validation" ) ) +{ +} + +void ValidationPropertyHelper::writeValidationProperties( PropertySet& rPropSet, const OoxValidationData& rValData ) +{ + Reference< XPropertySet > xValidation; + if( rPropSet.getProperty( xValidation, maValidationProp ) && xValidation.is() ) + { + PropertySet aValProps( xValidation ); + namespace csss = ::com::sun::star::sheet; + + // convert validation type to API enum + ValidationType eType = csss::ValidationType_ANY; + switch( rValData.mnType ) + { + case XML_custom: eType = csss::ValidationType_CUSTOM; break; + case XML_date: eType = csss::ValidationType_DATE; break; + case XML_decimal: eType = csss::ValidationType_DECIMAL; break; + case XML_list: eType = csss::ValidationType_LIST; break; + case XML_none: eType = csss::ValidationType_ANY; break; + case XML_textLength: eType = csss::ValidationType_TEXT_LEN; break; + case XML_time: eType = csss::ValidationType_TIME; break; + case XML_whole: eType = csss::ValidationType_WHOLE; break; + default: OSL_ENSURE( false, "ValidationPropertyHelper::writeValidationProperties - unknown validation type" ); + } + + // convert error alert style to API enum + ValidationAlertStyle nAlertStyle = csss::ValidationAlertStyle_STOP; + switch( rValData.mnErrorStyle ) + { + case XML_information: nAlertStyle = csss::ValidationAlertStyle_INFO; break; + case XML_stop: nAlertStyle = csss::ValidationAlertStyle_STOP; break; + case XML_warning: nAlertStyle = csss::ValidationAlertStyle_WARNING; break; + default: OSL_ENSURE( false, "ValidationPropertyHelper::writeValidationProperties - unknown error style" ); + } + + // convert dropdown style to API visibility constants + sal_Int16 nVisibility = rValData.mbNoDropDown ? csss::TableValidationVisibility::INVISIBLE : csss::TableValidationVisibility::UNSORTED; + + // write all properties + maValProps + << eType + << rValData.mbShowInputMsg << rValData.maInputTitle << rValData.maInputMessage + << rValData.mbShowErrorMsg << rValData.maErrorTitle << rValData.maErrorMessage + << nAlertStyle << nVisibility << rValData.mbAllowBlank + >> aValProps; + + try + { + // condition operator + Reference< XSheetCondition > xSheetCond( xValidation, UNO_QUERY_THROW ); + xSheetCond->setOperator( convertToApiOperator( rValData.mnOperator ) ); + + // condition formulas + Reference< XMultiFormulaTokens > xTokens( xValidation, UNO_QUERY_THROW ); + xTokens->setTokens( 0, rValData.maTokens1 ); + xTokens->setTokens( 1, rValData.maTokens2 ); + } + catch( Exception& ) + { + } + + // write back validation settings to cell range(s) + rPropSet.setProperty( maValidationProp, xValidation ); + } +} + +ConditionOperator ValidationPropertyHelper::convertToApiOperator( sal_Int32 nToken ) +{ + using namespace ::com::sun::star::sheet; + switch( nToken ) + { + case XML_between: return ConditionOperator_BETWEEN; + case XML_equal: return ConditionOperator_EQUAL; + case XML_greaterThan: return ConditionOperator_GREATER; + case XML_greaterThanOrEqual: return ConditionOperator_GREATER_EQUAL; + case XML_lessThan: return ConditionOperator_LESS; + case XML_lessThanOrEqual: return ConditionOperator_LESS_EQUAL; + case XML_notBetween: return ConditionOperator_NOT_BETWEEN; + case XML_notEqual: return ConditionOperator_NOT_EQUAL; + } + return ConditionOperator_NONE; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/viewsettings.cxx b/oox/source/xls/viewsettings.cxx new file mode 100644 index 000000000000..7ec207015ae6 --- /dev/null +++ b/oox/source/xls/viewsettings.cxx @@ -0,0 +1,789 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: viewsettings.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/viewsettings.hxx" +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/document/XViewDataSupplier.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/containerhelper.hxx" +#include "oox/helper/propertysequence.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/ooxtokens.hxx" +#include "oox/xls/unitconverter.hxx" +#include "oox/xls/workbooksettings.hxx" +#include "oox/xls/worksheetbuffer.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XNameContainer; +using ::com::sun::star::container::XIndexContainer; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::document::XViewDataSupplier; +using ::com::sun::star::table::CellAddress; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_Int32 OOX_BOOKVIEW_TABBARRATIO_DEF = 600; /// Default tabbar ratio. +const sal_Int32 OOX_SHEETVIEW_NORMALZOOM_DEF = 100; /// Default zoom for normal view. +const sal_Int32 OOX_SHEETVIEW_SHEETLAYZOOM_DEF = 60; /// Default zoom for pagebreak preview. +const sal_Int32 OOX_SHEETVIEW_PAGELAYZOOM_DEF = 100; /// Default zoom for page layout view. + +const sal_uInt8 OOBIN_PANE_FROZEN = 0x01; +const sal_uInt8 OOBIN_PANE_FROZENNOSPLIT = 0x02; + +const sal_uInt16 OOBIN_SHEETVIEW_WINPROTECTED = 0x0001; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWFORMULAS = 0x0002; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWGRID = 0x0004; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWHEADINGS = 0x0008; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWZEROS = 0x0010; +const sal_uInt16 OOBIN_SHEETVIEW_RIGHTTOLEFT = 0x0020; +const sal_uInt16 OOBIN_SHEETVIEW_SELECTED = 0x0040; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWRULER = 0x0080; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWOUTLINE = 0x0100; +const sal_uInt16 OOBIN_SHEETVIEW_DEFGRIDCOLOR = 0x0200; +const sal_uInt16 OOBIN_SHEETVIEW_SHOWWHITESPACE = 0x0400; + +const sal_uInt8 OOBIN_WBVIEW_HIDDEN = 0x01; +const sal_uInt8 OOBIN_WBVIEW_MINIMIZED = 0x02; +const sal_uInt8 OOBIN_WBVIEW_SHOWHORSCROLL = 0x08; +const sal_uInt8 OOBIN_WBVIEW_SHOWVERSCROLL = 0x10; +const sal_uInt8 OOBIN_WBVIEW_SHOWTABBAR = 0x20; +const sal_uInt8 OOBIN_WBVIEW_AUTOFILTERGROUP = 0x40; + +const sal_uInt8 BIFF_PANE_BOTTOMRIGHT = 0; /// Bottom-right pane. +const sal_uInt8 BIFF_PANE_TOPRIGHT = 1; /// Right, or top-right pane. +const sal_uInt8 BIFF_PANE_BOTTOMLEFT = 2; /// Bottom, or bottom-left pane. +const sal_uInt8 BIFF_PANE_TOPLEFT = 3; /// Single, top, left, or top-left pane. + +const sal_uInt16 BIFF_WINDOW1_HIDDEN = 0x0001; +const sal_uInt16 BIFF_WINDOW1_MINIMIZED = 0x0002; +const sal_uInt16 BIFF_WINDOW1_SHOWHORSCROLL = 0x0008; +const sal_uInt16 BIFF_WINDOW1_SHOWVERSCROLL = 0x0010; +const sal_uInt16 BIFF_WINDOW1_SHOWTABBAR = 0x0020; + +const sal_uInt16 BIFF_WINDOW2_SHOWFORMULAS = 0x0001; +const sal_uInt16 BIFF_WINDOW2_SHOWGRID = 0x0002; +const sal_uInt16 BIFF_WINDOW2_SHOWHEADINGS = 0x0004; +const sal_uInt16 BIFF_WINDOW2_FROZEN = 0x0008; +const sal_uInt16 BIFF_WINDOW2_SHOWZEROS = 0x0010; +const sal_uInt16 BIFF_WINDOW2_DEFGRIDCOLOR = 0x0020; +const sal_uInt16 BIFF_WINDOW2_RIGHTTOLEFT = 0x0040; +const sal_uInt16 BIFF_WINDOW2_SHOWOUTLINE = 0x0080; +const sal_uInt16 BIFF_WINDOW2_FROZENNOSPLIT = 0x0100; +const sal_uInt16 BIFF_WINDOW2_SELECTED = 0x0200; +const sal_uInt16 BIFF_WINDOW2_DISPLAYED = 0x0400; +const sal_uInt16 BIFF_WINDOW2_PAGEBREAKMODE = 0x0800; + +// Attention: view settings in Calc do not use com.sun.star.view.DocumentZoomType! +const sal_Int16 API_ZOOMTYPE_PERCENT = 0; /// Zoom value in percent. + +const sal_Int32 API_ZOOMVALUE_MIN = 20; /// Minimum zoom in Calc. +const sal_Int32 API_ZOOMVALUE_MAX = 400; /// Maximum zoom in Calc. + +// no predefined constants for split mode +const sal_Int16 API_SPLITMODE_NONE = 0; /// No splits in window. +const sal_Int16 API_SPLITMODE_SPLIT = 1; /// Window is split. +const sal_Int16 API_SPLITMODE_FREEZE = 2; /// Window has frozen panes. + +// no predefined constants for pane idetifiers +const sal_Int16 API_SPLITPANE_TOPLEFT = 0; /// Top-left, or top pane. +const sal_Int16 API_SPLITPANE_TOPRIGHT = 1; /// Top-right pane. +const sal_Int16 API_SPLITPANE_BOTTOMLEFT = 2; /// Bottom-left, bottom, left, or single pane. +const sal_Int16 API_SPLITPANE_BOTTOMRIGHT = 3; /// Bottom-right, or right pane. + +// ---------------------------------------------------------------------------- + +/** Property names for document view settings. */ +const sal_Char* const sppcDocNames[] = +{ + "Tables", + "ActiveTable", + "HasHorizontalScrollBar", + "HasVerticalScrollBar", + "HasSheetTabs", + "RelativeHorizontalTabbarWidth", + "ShowObjects", + "ShowCharts", + "ShowDrawing", + 0 +}; + +/** Property names for sheet view settings that are document-global in Calc. */ +const sal_Char* const sppcGlobalSheetNames[] = +{ + "GridColor", + "ZoomType", + "ZoomValue", + "PageViewZoomValue", + "ShowPageBreakPreview", + "ShowFormulas", + "ShowGrid", + "HasColumnRowHeaders", + "ShowZeroValues", + "IsOutlineSymbolsSet", + 0 +}; + +/** Property names for sheet view settings. */ +const sal_Char* const sppcSheetNames[] = +{ + "TableSelected", + "CursorPositionX", + "CursorPositionY", + "HorizontalSplitMode", + "VerticalSplitMode", + "HorizontalSplitPositionTwips", + "VerticalSplitPositionTwips", + "ActiveSplitRange", + "PositionLeft", + "PositionTop", + "PositionRight", + "PositionBottom", + 0 +}; + +// ---------------------------------------------------------------------------- + +/** Returns the OOXML pane identifier from the passed OOBIN or BIFF pane id. */ +sal_Int32 lclGetOoxPaneId( sal_Int32 nBinPaneId, sal_Int32 nDefaultPaneId ) +{ + static const sal_Int32 spnPaneIds[] = { XML_bottomRight, XML_topRight, XML_bottomLeft, XML_topLeft }; + return STATIC_ARRAY_SELECT( spnPaneIds, nBinPaneId, nDefaultPaneId ); +} + +} // namespace + +// ============================================================================ + +OoxSheetSelectionData::OoxSheetSelectionData() : + mnActiveCellId( 0 ) +{ +} + +// ---------------------------------------------------------------------------- + +OoxSheetViewData::OoxSheetViewData() : + maGridColor( XML_indexed, OOX_COLOR_WINDOWTEXT ), + mnWorkbookViewId( 0 ), + mnViewType( XML_normal ), + mnActivePaneId( XML_topLeft ), + mnPaneState( XML_split ), + mfSplitX( 0.0 ), + mfSplitY( 0.0 ), + mnCurrentZoom( 0 ), + mnNormalZoom( 0 ), + mnSheetLayoutZoom( 0 ), + mnPageLayoutZoom( 0 ), + mbSelected( false ), + mbRightToLeft( false ), + mbDefGridColor( true ), + mbShowFormulas( false ), + mbShowGrid( true ), + mbShowHeadings( true ), + mbShowZeros( true ), + mbShowOutline( true ) +{ +} + +bool OoxSheetViewData::isPageBreakPreview() const +{ + return mnViewType == XML_pageBreakPreview; +} + +sal_Int32 OoxSheetViewData::getNormalZoom() const +{ + const sal_Int32& rnZoom = isPageBreakPreview() ? mnNormalZoom : mnCurrentZoom; + sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_NORMALZOOM_DEF; + return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX ); +} + +sal_Int32 OoxSheetViewData::getPageBreakZoom() const +{ + const sal_Int32& rnZoom = isPageBreakPreview() ? mnCurrentZoom : mnSheetLayoutZoom; + sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_SHEETLAYZOOM_DEF; + return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX ); +} + +const OoxSheetSelectionData* OoxSheetViewData::getSelectionData( sal_Int32 nPaneId ) const +{ + return maSelMap.get( nPaneId ).get(); +} + +const OoxSheetSelectionData* OoxSheetViewData::getActiveSelectionData() const +{ + return getSelectionData( mnActivePaneId ); +} + +OoxSheetSelectionData& OoxSheetViewData::createSelectionData( sal_Int32 nPaneId ) +{ + OoxSelectionDataMap::mapped_type& rxSelData = maSelMap[ nPaneId ]; + if( !rxSelData ) + rxSelData.reset( new OoxSheetSelectionData ); + return *rxSelData; +} + +// ---------------------------------------------------------------------------- + +SheetViewSettings::SheetViewSettings( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ) +{ +} + +void SheetViewSettings::importSheetView( const AttributeList& rAttribs ) +{ + OoxSheetViewData& rData = *createSheetViewData(); + rData.maGridColor.set( XML_indexed, rAttribs.getInteger( XML_colorId, OOX_COLOR_WINDOWTEXT ) ); + rData.maFirstPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell ), getSheetIndex(), false ); + rData.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 ); + rData.mnViewType = rAttribs.getToken( XML_view, XML_normal ); + rData.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 ); + rData.mnNormalZoom = rAttribs.getInteger( XML_zoomScaleNormal, 0 ); + rData.mnSheetLayoutZoom = rAttribs.getInteger( XML_zoomScaleSheetLayoutView, 0 ); + rData.mnPageLayoutZoom = rAttribs.getInteger( XML_zoomScalePageLayoutView, 0 ); + rData.mbSelected = rAttribs.getBool( XML_tabSelected, false ); + rData.mbRightToLeft = rAttribs.getBool( XML_rightToLeft, false ); + rData.mbDefGridColor = rAttribs.getBool( XML_defaultGridColor, true ); + rData.mbShowFormulas = rAttribs.getBool( XML_showFormulas, false ); + rData.mbShowGrid = rAttribs.getBool( XML_showGridLines, true ); + rData.mbShowHeadings = rAttribs.getBool( XML_showRowColHeaders, true ); + rData.mbShowZeros = rAttribs.getBool( XML_showZeros, true ); + rData.mbShowOutline = rAttribs.getBool( XML_showOutlineSymbols, true ); +} + +void SheetViewSettings::importPane( const AttributeList& rAttribs ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importPane - missing view data" ); + if( !maSheetDatas.empty() ) + { + OoxSheetViewData& rData = *maSheetDatas.back(); + rData.maSecondPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell ), getSheetIndex(), false ); + rData.mnActivePaneId = rAttribs.getToken( XML_activePane, XML_topLeft ); + rData.mnPaneState = rAttribs.getToken( XML_state, XML_split ); + rData.mfSplitX = rAttribs.getDouble( XML_xSplit, 0.0 ); + rData.mfSplitY = rAttribs.getDouble( XML_ySplit, 0.0 ); + } +} + +void SheetViewSettings::importSelection( const AttributeList& rAttribs ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importSelection - missing view data" ); + if( !maSheetDatas.empty() ) + { + // pane this selection belongs to + sal_Int32 nPaneId = rAttribs.getToken( XML_pane, XML_topLeft ); + OoxSheetSelectionData& rSelData = maSheetDatas.back()->createSelectionData( nPaneId ); + // cursor position + rSelData.maActiveCell = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_activeCell ), getSheetIndex(), false ); + rSelData.mnActiveCellId = rAttribs.getInteger( XML_activeCellId, 0 ); + // selection + rSelData.maSelection.clear(); + getAddressConverter().convertToCellRangeList( rSelData.maSelection, rAttribs.getString( XML_sqref ), getSheetIndex(), false ); + } +} + +void SheetViewSettings::importSheetView( RecordInputStream& rStrm ) +{ + OoxSheetViewData& rData = *createSheetViewData(); + sal_uInt16 nFlags; + sal_Int32 nViewType; + BinAddress aFirstPos; + rStrm >> nFlags >> nViewType >> aFirstPos; + rData.maGridColor.importColorId( rStrm ); + rData.mnCurrentZoom = rStrm.readuInt16(); + rData.mnNormalZoom = rStrm.readuInt16(); + rData.mnSheetLayoutZoom = rStrm.readuInt16(); + rData.mnPageLayoutZoom = rStrm.readuInt16(); + rStrm >> rData.mnWorkbookViewId; + + rData.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false ); + static const sal_Int32 spnViewTypes[] = { XML_normal, XML_pageBreakPreview, XML_pageLayout }; + rData.mnViewType = STATIC_ARRAY_SELECT( spnViewTypes, nViewType, XML_normal ); + rData.mbSelected = getFlag( nFlags, OOBIN_SHEETVIEW_SELECTED ); + rData.mbRightToLeft = getFlag( nFlags, OOBIN_SHEETVIEW_RIGHTTOLEFT ); + rData.mbDefGridColor = getFlag( nFlags, OOBIN_SHEETVIEW_DEFGRIDCOLOR ); + rData.mbShowFormulas = getFlag( nFlags, OOBIN_SHEETVIEW_SHOWFORMULAS ); + rData.mbShowGrid = getFlag( nFlags, OOBIN_SHEETVIEW_SHOWGRID ); + rData.mbShowHeadings = getFlag( nFlags, OOBIN_SHEETVIEW_SHOWHEADINGS ); + rData.mbShowZeros = getFlag( nFlags, OOBIN_SHEETVIEW_SHOWZEROS ); + rData.mbShowOutline = getFlag( nFlags, OOBIN_SHEETVIEW_SHOWOUTLINE ); +} + +void SheetViewSettings::importPane( RecordInputStream& rStrm ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importPane - missing view data" ); + if( !maSheetDatas.empty() ) + { + OoxSheetViewData& rData = *maSheetDatas.back(); + + BinAddress aSecondPos; + sal_Int32 nActivePaneId; + sal_uInt8 nFlags; + rStrm >> rData.mfSplitX >> rData.mfSplitY >> aSecondPos >> nActivePaneId >> nFlags; + + rData.maSecondPos = getAddressConverter().createValidCellAddress( aSecondPos, getSheetIndex(), false ); + rData.mnActivePaneId = lclGetOoxPaneId( nActivePaneId, XML_topLeft ); + rData.mnPaneState = getFlagValue( nFlags, OOBIN_PANE_FROZEN, getFlagValue( nFlags, OOBIN_PANE_FROZENNOSPLIT, XML_frozen, XML_frozenSplit ), XML_split ); + } +} + +void SheetViewSettings::importSelection( RecordInputStream& rStrm ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importSelection - missing view data" ); + if( !maSheetDatas.empty() ) + { + // pane this selection belongs to + sal_Int32 nPaneId = rStrm.readInt32(); + OoxSheetSelectionData& rSelData = maSheetDatas.back()->createSelectionData( lclGetOoxPaneId( nPaneId, -1 ) ); + // cursor position + BinAddress aActiveCell; + rStrm >> aActiveCell >> rSelData.mnActiveCellId; + rSelData.maActiveCell = getAddressConverter().createValidCellAddress( aActiveCell, getSheetIndex(), false ); + // selection + BinRangeList aSelection; + rStrm >> aSelection; + rSelData.maSelection.clear(); + getAddressConverter().convertToCellRangeList( rSelData.maSelection, aSelection, getSheetIndex(), false ); + } +} + +void SheetViewSettings::importWindow2( BiffInputStream& rStrm ) +{ + OSL_ENSURE( maSheetDatas.empty(), "SheetViewSettings::importWindow2 - multiple WINDOW2 records" ); + OoxSheetViewData& rData = *createSheetViewData(); + if( getBiff() == BIFF2 ) + { + rData.mbShowFormulas = rStrm.readuInt8() != 0; + rData.mbShowGrid = rStrm.readuInt8() != 0; + rData.mbShowHeadings = rStrm.readuInt8() != 0; + rData.mnPaneState = (rStrm.readuInt8() == 0) ? XML_split : XML_frozen; + rData.mbShowZeros = rStrm.readuInt8() != 0; + BinAddress aFirstPos; + rStrm >> aFirstPos; + rData.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false ); + rData.mbDefGridColor = rStrm.readuInt8() != 0; + rData.maGridColor.importColorRgb( rStrm ); + } + else + { + sal_uInt16 nFlags; + BinAddress aFirstPos; + rStrm >> nFlags >> aFirstPos; + + rData.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false ); + rData.mnViewType = getFlagValue( nFlags, BIFF_WINDOW2_PAGEBREAKMODE, XML_pageBreakPreview, XML_normal ); + rData.mnPaneState = getFlagValue( nFlags, BIFF_WINDOW2_FROZEN, getFlagValue( nFlags, BIFF_WINDOW2_FROZENNOSPLIT, XML_frozen, XML_frozenSplit ), XML_split ); + rData.mbSelected = getFlag( nFlags, BIFF_WINDOW2_SELECTED ); + rData.mbRightToLeft = getFlag( nFlags, BIFF_WINDOW2_RIGHTTOLEFT ); + rData.mbDefGridColor = getFlag( nFlags, BIFF_WINDOW2_DEFGRIDCOLOR ); + rData.mbShowFormulas = getFlag( nFlags, BIFF_WINDOW2_SHOWFORMULAS ); + rData.mbShowGrid = getFlag( nFlags, BIFF_WINDOW2_SHOWGRID ); + rData.mbShowHeadings = getFlag( nFlags, BIFF_WINDOW2_SHOWHEADINGS ); + rData.mbShowZeros = getFlag( nFlags, BIFF_WINDOW2_SHOWZEROS ); + rData.mbShowOutline = getFlag( nFlags, BIFF_WINDOW2_SHOWOUTLINE ); + + if( getBiff() == BIFF8 ) + { + rData.maGridColor.importColorId( rStrm ); + // zoom data not included in chart sheets + if( rStrm.getRecLeft() >= 6 ) + { + rStrm.skip( 2 ); + sal_uInt16 nPageZoom, nNormalZoom; + rStrm >> nPageZoom >> nNormalZoom; + rData.mnSheetLayoutZoom = nPageZoom; + rData.mnNormalZoom = nNormalZoom; + } + } + else + { + rData.maGridColor.importColorRgb( rStrm ); + } + } +} + +void SheetViewSettings::importPane( BiffInputStream& rStrm ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importPane - missing leading WINDOW2 record" ); + if( !maSheetDatas.empty() ) + { + sal_uInt8 nActivePaneId; + sal_uInt16 nSplitX, nSplitY; + BinAddress aSecondPos; + rStrm >> nSplitX >> nSplitY >> aSecondPos >> nActivePaneId; + + OoxSheetViewData& rData = *maSheetDatas.back(); + rData.mfSplitX = nSplitX; + rData.mfSplitY = nSplitY; + rData.maSecondPos = getAddressConverter().createValidCellAddress( aSecondPos, getSheetIndex(), false ); + rData.mnActivePaneId = lclGetOoxPaneId( nActivePaneId, XML_topLeft ); + } +} + +void SheetViewSettings::importScl( BiffInputStream& rStrm ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importScl - missing leading WINDOW2 record" ); + if( !maSheetDatas.empty() ) + { + sal_uInt16 nNum, nDenom; + rStrm >> nNum >> nDenom; + OSL_ENSURE( nDenom > 0, "SheetViewSettings::importScl - invalid denominator" ); + if( nDenom > 0 ) + maSheetDatas.back()->mnCurrentZoom = getLimitedValue< sal_Int32, sal_uInt16 >( (nNum * 100) / nDenom, 10, 400 ); + } +} + +void SheetViewSettings::importSelection( BiffInputStream& rStrm ) +{ + OSL_ENSURE( !maSheetDatas.empty(), "SheetViewSettings::importPane - missing leading WINDOW2 record" ); + if( !maSheetDatas.empty() ) + { + // pane this selection belongs to + sal_uInt8 nPaneId = rStrm.readuInt8(); + OoxSheetSelectionData& rSelData = maSheetDatas.back()->createSelectionData( lclGetOoxPaneId( nPaneId, -1 ) ); + // cursor position + BinAddress aActiveCell; + sal_uInt16 nActiveCellId; + rStrm >> aActiveCell >> nActiveCellId; + rSelData.maActiveCell = getAddressConverter().createValidCellAddress( aActiveCell, getSheetIndex(), false ); + rSelData.mnActiveCellId = nActiveCellId; + // selection + rSelData.maSelection.clear(); + BinRangeList aSelection; + aSelection.read( rStrm, false ); + getAddressConverter().convertToCellRangeList( rSelData.maSelection, aSelection, getSheetIndex(), false ); + } +} + +void SheetViewSettings::finalizeImport() +{ + // force creation of sheet view data to get the Excel defaults + OoxSheetViewDataRef xData = maSheetDatas.empty() ? createSheetViewData() : maSheetDatas.front(); + + // mirrored sheet (this is not a view setting in Calc) + // #i59590# real life: Excel ignores mirror flag in chart sheets + if( xData->mbRightToLeft && (getSheetType() != SHEETTYPE_CHART) ) + { + PropertySet aPropSet( getXSpreadsheet() ); + aPropSet.setProperty( CREATE_OUSTRING( "TableLayout" ), ::com::sun::star::text::WritingMode2::RL_TB ); + } + + // sheet selected (active sheet must be selected) + bool bSelected = xData->mbSelected || (getSheetIndex() == getViewSettings().getActiveSheetIndex()); + + // current cursor position (selection not supported via API) + const OoxSheetSelectionData* pSelData = xData->getActiveSelectionData(); + CellAddress aCursor = pSelData ? pSelData->maActiveCell : xData->maFirstPos; + + // freeze/split position + sal_Int16 nHSplitMode = API_SPLITMODE_NONE; + sal_Int16 nVSplitMode = API_SPLITMODE_NONE; + sal_Int32 nHSplitPos = 0; + sal_Int32 nVSplitPos = 0; + if( (xData->mnPaneState == XML_frozen) || (xData->mnPaneState == XML_frozenSplit) ) + { + /* Frozen panes: handle split position as row/column positions. + #i35812# Excel uses number of visible rows/columns in the + frozen area (rows/columns scolled outside are not incuded), + Calc uses absolute position of first unfrozen row/column. */ + const CellAddress& rMaxApiPos = getAddressConverter().getMaxApiAddress(); + if( (xData->mfSplitX >= 1.0) && (xData->maFirstPos.Column + xData->mfSplitX <= rMaxApiPos.Column) ) + nHSplitPos = static_cast< sal_Int32 >( xData->maFirstPos.Column + xData->mfSplitX ); + nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE; + if( (xData->mfSplitY >= 1.0) && (xData->maFirstPos.Row + xData->mfSplitY <= rMaxApiPos.Row) ) + nVSplitPos = static_cast< sal_Int32 >( xData->maFirstPos.Row + xData->mfSplitY ); + nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE; + } + else if( xData->mnPaneState == XML_split ) + { + // split window: view settings API uses twips... + nHSplitPos = getLimitedValue< sal_Int32, double >( xData->mfSplitX + 0.5, 0, SAL_MAX_INT32 ); + nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE; + nVSplitPos = getLimitedValue< sal_Int32, double >( xData->mfSplitY + 0.5, 0, SAL_MAX_INT32 ); + nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE; + } + + // active pane + sal_Int16 nActivePane = API_SPLITPANE_BOTTOMLEFT; + switch( xData->mnActivePaneId ) + { + // no horizontal split -> always use left panes + // no vertical split -> always use *bottom* panes + case XML_topLeft: + nActivePane = (nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT; + break; + case XML_topRight: + nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ? + ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT) : + ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMRIGHT : API_SPLITPANE_TOPRIGHT); + break; + case XML_bottomLeft: + nActivePane = API_SPLITPANE_BOTTOMLEFT; + break; + case XML_bottomRight: + nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_BOTTOMRIGHT; + break; + } + + // automatic grid color + if( xData->mbDefGridColor ) + xData->maGridColor.set( XML_auto, 0 ); + + // write the sheet view settings into the property sequence + PropertySequence aSheetProps( sppcSheetNames, sppcGlobalSheetNames ); + aSheetProps + << bSelected + << aCursor.Column + << aCursor.Row + << nHSplitMode + << nVSplitMode + << nHSplitPos + << nVSplitPos + << nActivePane + << xData->maFirstPos.Column + << xData->maFirstPos.Row + << xData->maSecondPos.Column + << ((nVSplitPos > 0) ? xData->maSecondPos.Row : xData->maFirstPos.Row) + << getStyles().getColor( xData->maGridColor, API_RGB_TRANSPARENT ) + << API_ZOOMTYPE_PERCENT + << static_cast< sal_Int16 >( xData->getNormalZoom() ) + << static_cast< sal_Int16 >( xData->getPageBreakZoom() ) + << xData->isPageBreakPreview() + << xData->mbShowFormulas + << xData->mbShowGrid + << xData->mbShowHeadings + << xData->mbShowZeros + << xData->mbShowOutline; + + // store sheet view settings in global view settings object + getViewSettings().setSheetViewSettings( getSheetIndex(), xData, Any( aSheetProps.createPropertySequence() ) ); +} + +// private -------------------------------------------------------------------- + +OoxSheetViewDataRef SheetViewSettings::createSheetViewData() +{ + OoxSheetViewDataRef xData( new OoxSheetViewData ); + maSheetDatas.push_back( xData ); + return xData; +} + +// ============================================================================ + +OoxWorkbookViewData::OoxWorkbookViewData() : + mnWinX( 0 ), + mnWinY( 0 ), + mnWinWidth( 0 ), + mnWinHeight( 0 ), + mnActiveSheet( 0 ), + mnFirstVisSheet( 0 ), + mnTabBarWidth( OOX_BOOKVIEW_TABBARRATIO_DEF ), + mnVisibility( XML_visible ), + mbShowTabBar( true ), + mbShowHorScroll( true ), + mbShowVerScroll( true ), + mbMinimized( false ) +{ +} + +// ---------------------------------------------------------------------------- + +ViewSettings::ViewSettings( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void ViewSettings::importWorkbookView( const AttributeList& rAttribs ) +{ + OoxWorkbookViewData& rData = createWorkbookViewData(); + rData.mnWinX = rAttribs.getInteger( XML_xWindow, 0 ); + rData.mnWinY = rAttribs.getInteger( XML_yWindow, 0 ); + rData.mnWinWidth = rAttribs.getInteger( XML_windowWidth, 0 ); + rData.mnWinHeight = rAttribs.getInteger( XML_windowHeight, 0 ); + rData.mnActiveSheet = rAttribs.getInteger( XML_activeTab, 0 ); + rData.mnFirstVisSheet = rAttribs.getInteger( XML_firstSheet, 0 ); + rData.mnTabBarWidth = rAttribs.getInteger( XML_tabRatio, 600 ); + rData.mnVisibility = rAttribs.getToken( XML_visibility, XML_visible ); + rData.mbShowTabBar = rAttribs.getBool( XML_showSheetTabs, true ); + rData.mbShowHorScroll = rAttribs.getBool( XML_showHorizontalScroll, true ); + rData.mbShowVerScroll = rAttribs.getBool( XML_showVerticalScroll, true ); + rData.mbMinimized = rAttribs.getBool( XML_minimized, false ); +} + +void ViewSettings::importWorkbookView( RecordInputStream& rStrm ) +{ + OoxWorkbookViewData& rData = createWorkbookViewData(); + sal_uInt8 nFlags; + rStrm >> rData.mnWinX >> rData.mnWinY >> rData.mnWinWidth >> rData.mnWinHeight >> rData.mnTabBarWidth >> rData.mnFirstVisSheet >> rData.mnActiveSheet >> nFlags; + rData.mnVisibility = getFlagValue( nFlags, OOBIN_WBVIEW_HIDDEN, XML_hidden, XML_visible ); + rData.mbShowTabBar = getFlag( nFlags, OOBIN_WBVIEW_SHOWTABBAR ); + rData.mbShowHorScroll = getFlag( nFlags, OOBIN_WBVIEW_SHOWHORSCROLL ); + rData.mbShowVerScroll = getFlag( nFlags, OOBIN_WBVIEW_SHOWVERSCROLL ); + rData.mbMinimized = getFlag( nFlags, OOBIN_WBVIEW_MINIMIZED ); +} + +void ViewSettings::importWindow1( BiffInputStream& rStrm ) +{ + sal_uInt16 nWinX, nWinY, nWinWidth, nWinHeight; + rStrm >> nWinX >> nWinY >> nWinWidth >> nWinHeight; + + // WINDOW1 record occures in every sheet in BIFF4W + OSL_ENSURE( maBookDatas.empty() || ((getBiff() == BIFF4) && isWorkbookFile()), + "ViewSettings::importWindow1 - multiple WINDOW1 records" ); + OoxWorkbookViewData& rData = createWorkbookViewData(); + rData.mnWinX = nWinX; + rData.mnWinY = nWinY; + rData.mnWinWidth = nWinWidth; + rData.mnWinHeight = nWinHeight; + + if( getBiff() <= BIFF4 ) + { + sal_uInt8 nHidden; + rStrm >> nHidden; + rData.mnVisibility = (nHidden == 0) ? XML_visible : XML_hidden; + } + else + { + sal_uInt16 nFlags, nActiveTab, nFirstVisTab, nSelectCnt, nTabBarWidth; + rStrm >> nFlags >> nActiveTab >> nFirstVisTab >> nSelectCnt >> nTabBarWidth; + + rData.mnActiveSheet = nActiveTab; + rData.mnFirstVisSheet = nFirstVisTab; + rData.mnTabBarWidth = nTabBarWidth; + rData.mnVisibility = getFlagValue( nFlags, BIFF_WINDOW1_HIDDEN, XML_hidden, XML_visible ); + rData.mbMinimized = getFlag( nFlags, BIFF_WINDOW1_MINIMIZED ); + rData.mbShowHorScroll = getFlag( nFlags, BIFF_WINDOW1_SHOWHORSCROLL ); + rData.mbShowVerScroll = getFlag( nFlags, BIFF_WINDOW1_SHOWVERSCROLL ); + rData.mbShowTabBar = getFlag( nFlags, BIFF_WINDOW1_SHOWTABBAR ); + } +} + +void ViewSettings::setSheetViewSettings( sal_Int32 nSheet, const OoxSheetViewDataRef& rxViewData, const Any& rProperties ) +{ + maSheetDatas[ nSheet ] = rxViewData; + maSheetProps[ nSheet ] = rProperties; +} + +void ViewSettings::finalizeImport() +{ + const WorksheetBuffer& rWorksheets = getWorksheets(); + if( rWorksheets.getInternalSheetCount() <= 0 ) return; + + // force creation of workbook view data to get the Excel defaults + const OoxWorkbookViewData& rData = maBookDatas.empty() ? createWorkbookViewData() : *maBookDatas.front(); + + // show object mode is part of workbook settings + sal_Int16 nShowMode = getWorkbookSettings().getApiShowObjectMode(); + + // view settings for all sheets + Reference< XNameContainer > xSheetsNC = ContainerHelper::createNameContainer(); + if( !xSheetsNC.is() ) return; + for( SheetPropertiesMap::const_iterator aIt = maSheetProps.begin(), aEnd = maSheetProps.end(); aIt != aEnd; ++aIt ) + ContainerHelper::insertByName( xSheetsNC, rWorksheets.getFinalSheetName( aIt->first ), aIt->second ); + + // use data of active sheet to set sheet properties that are document-global in Calc + sal_Int32 nActiveSheet = getActiveSheetIndex(); + OoxSheetViewDataRef& rxActiveSheetData = maSheetDatas[ nActiveSheet ]; + OSL_ENSURE( rxActiveSheetData.get(), "ViewSettings::finalizeImport - missing active sheet view settings" ); + if( !rxActiveSheetData ) + rxActiveSheetData.reset( new OoxSheetViewData ); + + PropertySequence aDocProps( sppcDocNames, sppcGlobalSheetNames ); + aDocProps + << xSheetsNC + << rWorksheets.getFinalSheetName( nActiveSheet ) + << rData.mbShowHorScroll + << rData.mbShowVerScroll + << rData.mbShowTabBar + << double( rData.mnTabBarWidth / 1000.0 ) + << nShowMode << nShowMode << nShowMode + << getStyles().getColor( rxActiveSheetData->maGridColor, API_RGB_TRANSPARENT ) + << API_ZOOMTYPE_PERCENT + << static_cast< sal_Int16 >( rxActiveSheetData->getNormalZoom() ) + << static_cast< sal_Int16 >( rxActiveSheetData->getPageBreakZoom() ) + << rxActiveSheetData->isPageBreakPreview() + << rxActiveSheetData->mbShowFormulas + << rxActiveSheetData->mbShowGrid + << rxActiveSheetData->mbShowHeadings + << rxActiveSheetData->mbShowZeros + << rxActiveSheetData->mbShowOutline; + + Reference< XIndexContainer > xContainer = ContainerHelper::createIndexContainer(); + if( xContainer.is() ) try + { + xContainer->insertByIndex( 0, Any( aDocProps.createPropertySequence() ) ); + Reference< XIndexAccess > xIAccess( xContainer, UNO_QUERY_THROW ); + Reference< XViewDataSupplier > xViewDataSuppl( getDocument(), UNO_QUERY_THROW ); + xViewDataSuppl->setViewData( xIAccess ); + } + catch( Exception& ) + { + OSL_ENSURE( false, "ViewSettings::finalizeImport - cannot create document view settings" ); + } +} + +sal_Int32 ViewSettings::getActiveSheetIndex() const +{ + sal_Int32 nSheetCount = getLimitedValue< sal_Int32, sal_Int32 >( getWorksheets().getInternalSheetCount(), 1, SAL_MAX_INT32 ); + return maBookDatas.empty() ? 0 : getLimitedValue< sal_Int32, sal_Int32 >( maBookDatas.front()->mnActiveSheet, 0, nSheetCount - 1 ); +} + +// private -------------------------------------------------------------------- + +OoxWorkbookViewData& ViewSettings::createWorkbookViewData() +{ + OoxWorkbookViewDataRef xData( new OoxWorkbookViewData ); + maBookDatas.push_back( xData ); + return *xData; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/webquerybuffer.cxx b/oox/source/xls/webquerybuffer.cxx new file mode 100644 index 000000000000..c4ce7953dacd --- /dev/null +++ b/oox/source/xls/webquerybuffer.cxx @@ -0,0 +1,207 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: webquerybuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/webquerybuffer.hxx" +#include "oox/helper/attributelist.hxx" + +#define DEBUG_OOX_WEBQUERY_BUFFER 1 + +#if DEBUG_OOX_WEBQUERY_BUFFER +#include <stdio.h> +#endif + +using ::rtl::OUString; + +namespace oox { +namespace xls { + +const sal_Int32 Connection::CONNECTION_ODBC_SOURCE = 1; +const sal_Int32 Connection::CONNECTION_DAO_SOURCE = 2; +const sal_Int32 Connection::CONNECTION_FILE_SOURCE = 3; +const sal_Int32 Connection::CONNECTION_WEBQUERY = 4; +const sal_Int32 Connection::CONNECTION_OLEDB_SOURCE = 5; +const sal_Int32 Connection::CONNECTION_TEXT_SOURCE = 6; +const sal_Int32 Connection::CONNECTION_ADO_RECORD_SET = 7; +const sal_Int32 Connection::CONNECTION_DSP = 8; + +// ============================================================================ + +WebQueryBuffer::WebQueryBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ + maQueryTableMap.clear(); +} + +void WebQueryBuffer::importQueryTable( const AttributeList& rAttribs ) +{ + OUString aName = rAttribs.getString( XML_name ); + if ( !aName.getLength() ) + return; + + QueryTable aQTable; + aQTable.mnConnectionId = rAttribs.getInteger( XML_connectionId, 0 ); + + maQueryTableMap.insert( QueryTableHashMap::value_type( aName, aQTable ) ); + + // All documented attributes of queryTable: + // adjustColumnWidth (bool) + // applyAlignmentFormats (bool) + // applyBorderFormats (bool) + // applyFontFormats (bool) + // applyNumberFormats (bool) + // applyPatternFormats (bool) + // applyWidthHeightFormats (bool) + // autoFormatId (unsigned int) + // backgroundRefresh (bool) + // connectionId (unsigned int) + // disableEdit (bool) + // disableRefresh (bool) + // fillFormulas (bool) + // firstBackgroundRefresh (bool) + // growShrinkType (insertClear, insertDelete, overwriteClear) + // headers (bool) + // intermediate (bool) + // name (string) + // preserveFormatting(bool) + // refreshOnLoad (bool) + // removeDataOnSave (bool) + // rowNumbers (bool) +} + +void WebQueryBuffer::importConnection( const AttributeList& rAttribs ) +{ + if ( !rAttribs.hasAttribute( XML_id ) || !rAttribs.hasAttribute( XML_name ) ) + { + mnCurConnId = -1; + return; + } + + sal_uInt32 nId = rAttribs.getUnsignedInteger( XML_id, 0 ); + if ( maConnections.size() < (nId + 1) ) + maConnections.resize(nId + 1); + + Connection aConn; + aConn.maName = rAttribs.getString( XML_name ); + aConn.mnType = rAttribs.getInteger( XML_type, 0 ); + maConnections[nId] = aConn; + mnCurConnId = nId; + + // All documented attributes of connection. + // background (bool) + // credentials (integrated, none, prompt, stored) + // deleted (bool) + // description (string) + // id (unsigned int) + // interval (unsigned int) + // keepAlive (bool) + // minRefreshableVersion (unsigned byte) + // name (string) + // new (bool) + // odcFile (string) + // onlyUseConnectionFile (bool) + // reconnectionMethod (unsigned int) + // refreshedVersion (unsigned byte) + // refreshOnLoad (bool) + // saveData (bool) + // savePassword (bool) + // singleSignOnId (string) + // sourceFile (string) + // type (unsigned int) +} + +void WebQueryBuffer::importWebPr( const AttributeList& rAttribs ) +{ + if ( 0 > mnCurConnId ) + return; + + Connection& rConn = maConnections[mnCurConnId]; + rConn.mpProperties.reset( new WebProperties ); + WebProperties* pWebPr = static_cast< WebProperties* >( rConn.mpProperties.get() ); + pWebPr->maURL = rAttribs.getString( XML_url ); + + // All available attributes: + // consecutive (bool) + // editPage (string) + // firstRow (bool) + // htmlFormat (all, none, rtf) + // htmlTables (bool) + // parsePre (bool) + // post (string) + // sourceData (bool) + // textDates (bool) + // url (string) + // xl2000 (bool) + // xl97 (bool) + // xml (bool) +} + +void WebQueryBuffer::dump() const +{ +#if DEBUG_OOX_WEBQUERY_BUFFER + fprintf(stdout, "----------------------------------------\n"); + { + using ::std::vector; + vector< Connection >::const_iterator itr = maConnections.begin(), itrEnd = maConnections.end(); + sal_Int32 nId = 0; + for (; itr != itrEnd; ++itr, ++nId) + { + if ( itr->mnType == Connection::CONNECTION_WEBQUERY ) + { + WebProperties* pWebPr = static_cast< WebProperties* >( itr->mpProperties.get() ); + fprintf(stdout, "WebQueryBuffer::dump: id = %d url = %s\n", + (int)nId, + OUStringToOString(pWebPr->maURL, RTL_TEXTENCODING_UTF8).getStr()); + } + } + } + + QueryTableHashMap::const_iterator itr = maQueryTableMap.begin(), itrEnd = maQueryTableMap.end(); + for (; itr != itrEnd; ++itr) + { + fprintf(stdout, "WebQueryBuffer::dump: name = %s connection ID = %d\n", + OUStringToOString(itr->first, RTL_TEXTENCODING_UTF8).getStr(), + (int)(itr->second.mnConnectionId)); + } + + fprintf(stdout, "----------------------------------------\n"); + fflush(stdout); +#endif +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/workbookfragment.cxx b/oox/source/xls/workbookfragment.cxx new file mode 100644 index 000000000000..838711bb1198 --- /dev/null +++ b/oox/source/xls/workbookfragment.cxx @@ -0,0 +1,756 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: workbookfragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/workbookfragment.hxx" +#include <com/sun/star/table/CellAddress.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/progressbar.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/drawingml/themefragmenthandler.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/connectionsfragment.hxx" +#include "oox/xls/externallinkbuffer.hxx" +#include "oox/xls/externallinkfragment.hxx" +#include "oox/xls/pivotcachefragment.hxx" +#include "oox/xls/sharedstringsbuffer.hxx" +#include "oox/xls/sharedstringsfragment.hxx" +#include "oox/xls/stylesfragment.hxx" +#include "oox/xls/tablebuffer.hxx" +#include "oox/xls/themebuffer.hxx" +#include "oox/xls/viewsettings.hxx" +#include "oox/xls/workbooksettings.hxx" +#include "oox/xls/worksheetbuffer.hxx" +#include "oox/xls/worksheetfragment.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::table::CellAddress; +using ::oox::core::FragmentHandlerRef; +using ::oox::core::Relation; +using ::oox::drawingml::ThemeFragmentHandler; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const double PROGRESS_LENGTH_GLOBALS = 0.1; /// 10% of progress bar for globals import. + +const sal_uInt16 BIFF_FILEPASS_BIFF2 = 0x0000; +const sal_uInt16 BIFF_FILEPASS_BIFF8 = 0x0001; +const sal_uInt16 BIFF_FILEPASS_BIFF8_RCF = 0x0001; +const sal_uInt16 BIFF_FILEPASS_BIFF8_STRONG = 0x0002; + +} // namespace + +// ============================================================================ + +OoxWorkbookFragment::OoxWorkbookFragment( + const WorkbookHelper& rHelper, const OUString& rFragmentPath ) : + OoxWorkbookFragmentBase( rHelper, rFragmentPath ) +{ +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxWorkbookFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nElement == XLS_TOKEN( workbook )); + case XLS_TOKEN( workbook ): + return (nElement == XLS_TOKEN( workbookPr )) || + (nElement == XLS_TOKEN( calcPr )) || + (nElement == XLS_TOKEN( sheets )) || + (nElement == XLS_TOKEN( bookViews )) || + (nElement == XLS_TOKEN( externalReferences )) || + (nElement == XLS_TOKEN( definedNames )) || + (nElement == XLS_TOKEN( pivotCaches )); + case XLS_TOKEN( sheets ): + return (nElement == XLS_TOKEN( sheet )); + case XLS_TOKEN( bookViews ): + return (nElement == XLS_TOKEN( workbookView )); + case XLS_TOKEN( externalReferences ): + return (nElement == XLS_TOKEN( externalReference )); + case XLS_TOKEN( definedNames ): + return (nElement == XLS_TOKEN( definedName )); + case XLS_TOKEN( pivotCaches ): + return (nElement == XLS_TOKEN( pivotCache )); + } + return false; +} + +void OoxWorkbookFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( workbookPr ): getWorkbookSettings().importWorkbookPr( rAttribs ); break; + case XLS_TOKEN( calcPr ): getWorkbookSettings().importCalcPr( rAttribs ); break; + case XLS_TOKEN( sheet ): getWorksheets().importSheet( rAttribs ); break; + case XLS_TOKEN( workbookView ): getViewSettings().importWorkbookView( rAttribs ); break; + case XLS_TOKEN( externalReference ): importExternalReference( rAttribs ); break; + case XLS_TOKEN( definedName ): importDefinedName( rAttribs ); break; + case XLS_TOKEN( pivotCache ): importPivotCache( rAttribs ); break; + } +} + +void OoxWorkbookFragment::onEndElement( const OUString& rChars ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( definedName ): + if( mxCurrName.get() ) mxCurrName->setFormula( rChars ); + break; + } +} + +bool OoxWorkbookFragment::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nRecId == OOBIN_ID_WORKBOOK); + case OOBIN_ID_WORKBOOK: + return (nRecId == OOBIN_ID_WORKBOOKPR) || + (nRecId == OOBIN_ID_CALCPR) || + (nRecId == OOBIN_ID_SHEETS) || + (nRecId == OOBIN_ID_BOOKVIEWS) || + (nRecId == OOBIN_ID_EXTERNALREFS) || + (nRecId == OOBIN_ID_DEFINEDNAME); + case OOBIN_ID_SHEETS: + return (nRecId == OOBIN_ID_SHEET); + case OOBIN_ID_BOOKVIEWS: + return (nRecId == OOBIN_ID_WORKBOOKVIEW); + case OOBIN_ID_EXTERNALREFS: + return (nRecId == OOBIN_ID_EXTERNALREF) || + (nRecId == OOBIN_ID_EXTERNALSELF) || + (nRecId == OOBIN_ID_EXTERNALSHEETS); + } + return false; +} + +void OoxWorkbookFragment::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_WORKBOOKPR: getWorkbookSettings().importWorkbookPr( rStrm ); break; + case OOBIN_ID_CALCPR: getWorkbookSettings().importCalcPr( rStrm ); break; + case OOBIN_ID_SHEET: getWorksheets().importSheet( rStrm ); break; + case OOBIN_ID_WORKBOOKVIEW: getViewSettings().importWorkbookView( rStrm ); break; + case OOBIN_ID_EXTERNALREF: importExternalRef( rStrm ); break; + case OOBIN_ID_EXTERNALSELF: getExternalLinks().importExternalSelf( rStrm ); break; + case OOBIN_ID_EXTERNALSHEETS: getExternalLinks().importExternalSheets( rStrm ); break; + case OOBIN_ID_DEFINEDNAME: getDefinedNames().importDefinedName( rStrm ); break; + } +} + +// oox.xls.OoxFragmentHandler interface --------------------------------------- + +void OoxWorkbookFragment::finalizeImport() +{ + ISegmentProgressBarRef xGlobalSegment = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS ); + + // read the theme substream + OUString aThemeFragmentPath = getFragmentPathFromType( CREATE_RELATIONS_TYPE( "theme" ) ); + if( aThemeFragmentPath.getLength() > 0 ) + importOoxFragment( new ThemeFragmentHandler( getFilter(), aThemeFragmentPath, getTheme().getCoreTheme() ) ); + xGlobalSegment->setPosition( 0.25 ); + + // read the styles substream (requires finalized theme buffer) + OUString aStylesFragmentPath = getFragmentPathFromType( CREATE_RELATIONS_TYPE( "styles" ) ); + if( aStylesFragmentPath.getLength() > 0 ) + importOoxFragment( new OoxStylesFragment( *this, aStylesFragmentPath ) ); + xGlobalSegment->setPosition( 0.5 ); + + // read the shared string table substream (requires finalized styles buffer) + OUString aSstFragmentPath = getFragmentPathFromType( CREATE_RELATIONS_TYPE( "sharedStrings" ) ); + if( aSstFragmentPath.getLength() > 0 ) + importOoxFragment( new OoxSharedStringsFragment( *this, aSstFragmentPath ) ); + xGlobalSegment->setPosition( 0.75 ); + + // read the connections substream + OUString aConnFragmentPath = getFragmentPathFromType( CREATE_RELATIONS_TYPE( "connections" ) ); + if( aConnFragmentPath.getLength() > 0 ) + importOoxFragment( new OoxConnectionsFragment( *this, aConnFragmentPath ) ); + xGlobalSegment->setPosition( 1.0 ); + + /* Create fragments for all sheets, before importing them. Needed to do + some preprocessing in the fragment constructors, e.g. loading the table + fragments for all sheets that are needed before the cell formulas are + loaded. */ + typedef ::std::map< sal_Int32, FragmentHandlerRef > SheetFragmentMap; + SheetFragmentMap aSheetFragments; + WorksheetBuffer& rWorksheets = getWorksheets(); + sal_Int32 nSheetCount = rWorksheets.getInternalSheetCount(); + for( sal_Int32 nSheet = 0; nSheet < nSheetCount; ++nSheet ) + { + if( const Relation* pRelation = getRelations().getRelationFromRelId( rWorksheets.getSheetRelId( nSheet ) ) ) + { + // get fragment path of the sheet + OUString aFragmentPath = getFragmentPathFromTarget( pRelation->maTarget ); + OSL_ENSURE( aFragmentPath.getLength() > 0, "OoxWorkbookFragment::finalizeImport - cannot access sheet fragment" ); + if( aFragmentPath.getLength() > 0 ) + { + ::rtl::Reference< OoxWorksheetFragmentBase > xFragment; + double fSegmentLength = getProgressBar().getFreeLength() / (nSheetCount - nSheet); + ISegmentProgressBarRef xSheetSegment = getProgressBar().createSegment( fSegmentLength ); + // create the fragment according to the sheet type + if( pRelation->maType == CREATE_RELATIONS_TYPE( "worksheet" ) ) + xFragment.set( new OoxWorksheetFragment( *this, aFragmentPath, xSheetSegment, SHEETTYPE_WORKSHEET, nSheet ) ); + // insert the fragment into the map + OSL_ENSURE( xFragment.is(), "OoxWorkbookFragment::finalizeImport - unknown sheet type" ); + OSL_ENSURE( !xFragment.is() || xFragment->isValidSheet(), "OoxWorkbookFragment::finalizeImport - missing sheet in document" ); + if( xFragment.is() && xFragment->isValidSheet() ) + aSheetFragments[ nSheet ].set( xFragment.get() ); + } + } + } + + // create all defined names and database ranges + getDefinedNames().finalizeImport(); + getTables().finalizeImport(); + + // load all worksheets + for( sal_Int32 nSheet = 0; nSheet < nSheetCount; ++nSheet ) + { + SheetFragmentMap::iterator aIt = aSheetFragments.find( nSheet ); + if( aIt != aSheetFragments.end() ) + { + // import the sheet fragment + importOoxFragment( aIt->second ); + // delete fragment object, will free all allocated sheet buffers + aSheetFragments.erase( aIt ); + } + } + + // final conversions, e.g. calculation settings and view settings + finalizeWorkbookImport(); + + getPivotTables().finalizeImport(); +} + +// private -------------------------------------------------------------------- + +void OoxWorkbookFragment::importExternalReference( const AttributeList& rAttribs ) +{ + if( ExternalLink* pExtLink = getExternalLinks().importExternalReference( rAttribs ).get() ) + importExternalLinkFragment( *pExtLink ); +} + +void OoxWorkbookFragment::importDefinedName( const AttributeList& rAttribs ) +{ + mxCurrName = getDefinedNames().importDefinedName( rAttribs ); +} + +void OoxWorkbookFragment::importPivotCache( const AttributeList& rAttribs ) +{ + OUString aFragmentPath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ) ) ); + if( (aFragmentPath.getLength() > 0) && rAttribs.hasAttribute( XML_cacheId ) ) + { + sal_uInt32 nCacheId = rAttribs.getUnsignedInteger( XML_cacheId, 0 ); + importOoxFragment( new OoxPivotCacheFragment( *this, aFragmentPath, nCacheId ) ); + } +} + +void OoxWorkbookFragment::importExternalRef( RecordInputStream& rStrm ) +{ + if( ExternalLink* pExtLink = getExternalLinks().importExternalRef( rStrm ).get() ) + importExternalLinkFragment( *pExtLink ); +} + +void OoxWorkbookFragment::importExternalLinkFragment( ExternalLink& rExtLink ) +{ + OUString aFragmentPath = getFragmentPathFromRelId( rExtLink.getRelId() ); + if( aFragmentPath.getLength() > 0 ) + importOoxFragment( new OoxExternalLinkFragment( *this, aFragmentPath, rExtLink ) ); +} + +// ============================================================================ + +namespace { + +BiffDecoderRef lclImportFilePass_XOR( const WorkbookHelper& rHelper, BiffInputStream& rStrm ) +{ + BiffDecoderRef xDecoder; + OSL_ENSURE( rStrm.getRecLeft() == 4, "lclImportFilePass_XOR - wrong record size" ); + if( rStrm.getRecLeft() == 4 ) + { + sal_uInt16 nBaseKey, nHash; + rStrm >> nBaseKey >> nHash; + xDecoder.reset( new BiffDecoder_XOR( rHelper, nBaseKey, nHash ) ); + } + return xDecoder; +} + +BiffDecoderRef lclImportFilePass_RCF( const WorkbookHelper& rHelper, BiffInputStream& rStrm ) +{ + BiffDecoderRef xDecoder; + OSL_ENSURE( rStrm.getRecLeft() == 48, "lclImportFilePass_RCF - wrong record size" ); + if( rStrm.getRecLeft() == 48 ) + { + sal_uInt8 pnDocId[ 16 ]; + sal_uInt8 pnSaltData[ 16 ]; + sal_uInt8 pnSaltHash[ 16 ]; + rStrm.read( pnDocId, 16 ); + rStrm.read( pnSaltData, 16 ); + rStrm.read( pnSaltHash, 16 ); + xDecoder.reset( new BiffDecoder_RCF( rHelper, pnDocId, pnSaltData, pnSaltHash ) ); + } + return xDecoder; +} + +BiffDecoderRef lclImportFilePass_Strong( const WorkbookHelper& /*rHelper*/, BiffInputStream& /*rStrm*/ ) +{ + // not supported + return BiffDecoderRef(); +} + +BiffDecoderRef lclImportFilePass2( const WorkbookHelper& rHelper, BiffInputStream& rStrm ) +{ + return lclImportFilePass_XOR( rHelper, rStrm ); +} + +BiffDecoderRef lclImportFilePass8( const WorkbookHelper& rHelper, BiffInputStream& rStrm ) +{ + BiffDecoderRef xDecoder; + + switch( rStrm.readuInt16() ) + { + case BIFF_FILEPASS_BIFF2: + xDecoder = lclImportFilePass_XOR( rHelper, rStrm ); + break; + + case BIFF_FILEPASS_BIFF8: + switch( rStrm.skip( 2 ).readuInt16() ) + { + case BIFF_FILEPASS_BIFF8_RCF: + xDecoder = lclImportFilePass_RCF( rHelper, rStrm ); + break; + case BIFF_FILEPASS_BIFF8_STRONG: + xDecoder = lclImportFilePass_Strong( rHelper, rStrm ); + break; + default: + OSL_ENSURE( false, "lclImportFilePass8 - unknown BIFF8 encryption sub mode" ); + } + break; + + default: + OSL_ENSURE( false, "lclImportFilePass8 - unknown encryption mode" ); + } + + return xDecoder; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +BiffWorkbookFragment::BiffWorkbookFragment( const WorkbookHelper& rHelper ) : + BiffWorkbookFragmentBase( rHelper ) +{ +} + +bool BiffWorkbookFragment::importFragment( BiffInputStream& rStrm ) +{ + bool bRet = false; + + BiffFragmentType eFragment = startFragment( rStrm, getBiff() ); + switch( eFragment ) + { + case BIFF_FRAGMENT_GLOBALS: + { + // import workbook globals fragment and create sheets in document + ISegmentProgressBarRef xGlobalsProgress = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS ); + bRet = importGlobalsFragment( rStrm, *xGlobalsProgress ); + // load sheet fragments (do not return false in bRet on missing/broken sheets) + WorksheetBuffer& rWorksheets = getWorksheets(); + bool bNextSheet = bRet; + for( sal_Int32 nSheet = 0, nSheetCount = rWorksheets.getInternalSheetCount(); bNextSheet && (nSheet < nSheetCount); ++nSheet ) + { + // try to start a new sheet fragment + double fSegmentLength = getProgressBar().getFreeLength() / (nSheetCount - nSheet); + ISegmentProgressBarRef xSheetProgress = getProgressBar().createSegment( fSegmentLength ); + BiffFragmentType eSheetFragment = startFragment( rStrm, getBiff() ); + bNextSheet = importSheetFragment( rStrm, *xSheetProgress, eSheetFragment, nSheet ); + } + } + break; + + case BIFF_FRAGMENT_WORKSPACE: + { + bRet = importWorkspaceFragment( rStrm ); + // sheets are embedded in workspace fragment, nothing to do here + } + break; + + case BIFF_FRAGMENT_WORKSHEET: + case BIFF_FRAGMENT_CHART: + case BIFF_FRAGMENT_MACRO: + { + /* Single sheet without globals + - #i62752# possible in all BIFF versions + - do not return false in bRet on missing/broken sheets. */ + getWorksheets().initializeSingleSheet(); + importSheetFragment( rStrm, getProgressBar(), eFragment, 0 ); + // success, even if stream is broken + bRet = true; + } + break; + + default:; + } + + // final conversions, e.g. calculation settings and view settings + finalizeWorkbookImport(); + + return bRet; +} + +bool BiffWorkbookFragment::importWorkspaceFragment( BiffInputStream& rStrm ) +{ + // enable workbook mode, has not been set yet in BIFF4 workspace files + setIsWorkbookFile(); + + WorksheetBuffer& rWorksheets = getWorksheets(); + bool bRet = true; + + // import the workspace globals + ISegmentProgressBarRef xGlobalsProgress = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS ); + bool bLoop = true; + while( bRet && bLoop && rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) ) + { + switch( rStrm.getRecId() ) + { + case BIFF_ID_SHEET: rWorksheets.importSheet( rStrm ); break; + case BIFF_ID_CODEPAGE: setCodePage( rStrm.readuInt16() ); break; + case BIFF_ID_FILEPASS: bRet = importFilePass( rStrm ); break; + case BIFF_ID_SHEETHEADER: rStrm.rewindRecord(); bLoop = false; break; + } + } + xGlobalsProgress->setPosition( 1.0 ); + + // load sheet fragments (do not return false in bRet on missing/broken sheets) + bool bNextSheet = bRet; + for( sal_Int32 nSheet = 0, nSheetCount = rWorksheets.getInternalSheetCount(); bNextSheet && (nSheet < nSheetCount); ++nSheet ) + { + // try to start a new sheet fragment (with leading SHEETHEADER record) + bNextSheet = rStrm.startNextRecord() && (rStrm.getRecId() == BIFF_ID_SHEETHEADER); + if( bNextSheet ) + { + double fSegmentLength = getProgressBar().getFreeLength() / (nSheetCount - nSheet); + ISegmentProgressBarRef xSheetProgress = getProgressBar().createSegment( fSegmentLength ); + /* Read current sheet name (sheet substreams may not be in the + same order as SHEET records are). */ + OUString aSheetName = rStrm.skip( 4 ).readByteString( false, getTextEncoding() ); + sal_Int32 nCurrSheet = rWorksheets.getFinalSheetIndex( aSheetName ); + // load the sheet fragment records + BiffFragmentType eSheetFragment = startFragment( rStrm, getBiff() ); + bNextSheet = importSheetFragment( rStrm, *xSheetProgress, eSheetFragment, nCurrSheet ); + // do not return false in bRet on missing/broken sheets + } + } + + return bRet; +} + +bool BiffWorkbookFragment::importGlobalsFragment( BiffInputStream& rStrm, ISegmentProgressBar& rProgressBar ) +{ + WorkbookSettings& rWorkbookSett = getWorkbookSettings(); + ViewSettings& rViewSett = getViewSettings(); + SharedStringsBuffer& rSharedStrings = getSharedStrings(); + StylesBuffer& rStyles = getStyles(); + WorksheetBuffer& rWorksheets = getWorksheets(); + + // collect records that need to be loaded in a second pass + typedef ::std::vector< sal_Int64 > RecordHandleVec; + RecordHandleVec aExtLinkRecs; + + bool bRet = true; + bool bLoop = true; + while( bRet && bLoop && rStrm.startNextRecord() ) + { + sal_uInt16 nRecId = rStrm.getRecId(); + bool bExtLinkRec = false; + + /* #i56376# BIFF5-BIFF8: If an EOF record for globals is missing, + simulate it. The issue is about a document where the sheet fragment + starts directly after the EXTSST record, without terminating the + globals fragment with an EOF record. */ + if( isBofRecord( nRecId ) || (nRecId == BIFF_ID_EOF) ) + { + bLoop = false; + } + else switch( nRecId ) + { + // records in all BIFF versions + case BIFF_ID_CODEPAGE: setCodePage( rStrm.readuInt16() ); break; + case BIFF_ID_DATEMODE: rWorkbookSett.importDateMode( rStrm ); break; + case BIFF_ID_FILEPASS: bRet = importFilePass( rStrm ); break; + case BIFF_ID_PRECISION: rWorkbookSett.importPrecision( rStrm ); break; + case BIFF_ID_WINDOW1: rViewSett.importWindow1( rStrm ); break; + + // BIFF specific records + default: switch( getBiff() ) + { + case BIFF2: switch( nRecId ) + { + case BIFF2_ID_DEFINEDNAME: bExtLinkRec = true; break; + case BIFF2_ID_EXTERNALNAME: bExtLinkRec = true; break; + case BIFF_ID_EXTERNSHEET: bExtLinkRec = true; break; + case BIFF2_ID_FONT: rStyles.importFont( rStrm ); break; + case BIFF_ID_FONTCOLOR: rStyles.importFontColor( rStrm ); break; + case BIFF2_ID_FORMAT: rStyles.importFormat( rStrm ); break; + case BIFF2_ID_XF: rStyles.importXf( rStrm ); break; + } + break; + + case BIFF3: switch( nRecId ) + { + case BIFF_ID_CRN: bExtLinkRec = true; break; + case BIFF3_ID_DEFINEDNAME: bExtLinkRec = true; break; + case BIFF3_ID_EXTERNALNAME: bExtLinkRec = true; break; + case BIFF_ID_EXTERNSHEET: bExtLinkRec = true; break; + case BIFF3_ID_FONT: rStyles.importFont( rStrm ); break; + case BIFF2_ID_FORMAT: rStyles.importFormat( rStrm ); break; + case BIFF_ID_HIDEOBJ: rWorkbookSett.importHideObj( rStrm ); break; + case BIFF_ID_PALETTE: rStyles.importPalette( rStrm ); break; + case BIFF_ID_STYLE: rStyles.importStyle( rStrm ); break; + case BIFF_ID_XCT: bExtLinkRec = true; break; + case BIFF3_ID_XF: rStyles.importXf( rStrm ); break; + } + break; + + case BIFF4: switch( nRecId ) + { + case BIFF_ID_CRN: bExtLinkRec = true; break; + case BIFF3_ID_DEFINEDNAME: bExtLinkRec = true; break; + case BIFF3_ID_EXTERNALNAME: bExtLinkRec = true; break; + case BIFF_ID_EXTERNSHEET: bExtLinkRec = true; break; + case BIFF3_ID_FONT: rStyles.importFont( rStrm ); break; + case BIFF4_ID_FORMAT: rStyles.importFormat( rStrm ); break; + case BIFF_ID_HIDEOBJ: rWorkbookSett.importHideObj( rStrm ); break; + case BIFF_ID_PALETTE: rStyles.importPalette( rStrm ); break; + case BIFF_ID_STYLE: rStyles.importStyle( rStrm ); break; + case BIFF_ID_XCT: bExtLinkRec = true; break; + case BIFF4_ID_XF: rStyles.importXf( rStrm ); break; + } + break; + + case BIFF5: switch( nRecId ) + { + case BIFF_ID_BOOKBOOL: rWorkbookSett.importBookBool( rStrm ); break; + case BIFF_ID_CRN: bExtLinkRec = true; break; + case BIFF5_ID_DEFINEDNAME: bExtLinkRec = true; break; + case BIFF5_ID_EXTERNALNAME: bExtLinkRec = true; break; + case BIFF_ID_EXTERNSHEET: bExtLinkRec = true; break; + case BIFF5_ID_FONT: rStyles.importFont( rStrm ); break; + case BIFF4_ID_FORMAT: rStyles.importFormat( rStrm ); break; + case BIFF_ID_HIDEOBJ: rWorkbookSett.importHideObj( rStrm ); break; + case BIFF_ID_PALETTE: rStyles.importPalette( rStrm ); break; + case BIFF_ID_SHEET: rWorksheets.importSheet( rStrm ); break; + case BIFF_ID_STYLE: rStyles.importStyle( rStrm ); break; + case BIFF_ID_XCT: bExtLinkRec = true; break; + case BIFF5_ID_XF: rStyles.importXf( rStrm ); break; + } + break; + + case BIFF8: switch( nRecId ) + { + case BIFF_ID_BOOKBOOL: rWorkbookSett.importBookBool( rStrm ); break; + case BIFF_ID_CODENAME: rWorkbookSett.importCodeName( rStrm ); break; + case BIFF_ID_CRN: bExtLinkRec = true; break; + case BIFF5_ID_DEFINEDNAME: bExtLinkRec = true; break; + case BIFF_ID_EXTERNALBOOK: bExtLinkRec = true; break; + case BIFF5_ID_EXTERNALNAME: bExtLinkRec = true; break; + case BIFF_ID_EXTERNSHEET: bExtLinkRec = true; break; + case BIFF5_ID_FONT: rStyles.importFont( rStrm ); break; + case BIFF4_ID_FORMAT: rStyles.importFormat( rStrm ); break; + case BIFF_ID_HIDEOBJ: rWorkbookSett.importHideObj( rStrm ); break; + case BIFF_ID_PALETTE: rStyles.importPalette( rStrm ); break; + case BIFF_ID_SHEET: rWorksheets.importSheet( rStrm ); break; + case BIFF_ID_SST: rSharedStrings.importSst( rStrm ); break; + case BIFF_ID_STYLE: rStyles.importStyle( rStrm ); break; + case BIFF_ID_USESELFS: rWorkbookSett.importUsesElfs( rStrm ); break; + case BIFF_ID_XCT: bExtLinkRec = true; break; + case BIFF5_ID_XF: rStyles.importXf( rStrm ); break; + } + break; + + case BIFF_UNKNOWN: break; + } + } + + if( bExtLinkRec ) + aExtLinkRecs.push_back( rStrm.getRecHandle() ); + } + + // finalize global buffers + rProgressBar.setPosition( 0.5 ); + rSharedStrings.finalizeImport(); + rStyles.finalizeImport(); + + /* Import external link data (EXTERNSHEET, EXTERNALNAME, DEFINEDNAME) + which need existing internal sheets (SHEET records). The SHEET records + may follow the external links records in some BIFF versions. */ + if( bRet && !aExtLinkRecs.empty() ) + { + // remember current stream position (the EOF record) + sal_Int64 nEofHandle = rStrm.getRecHandle(); + // this fragment class implements import of external link records + BiffExternalLinkFragment aLinkFragment( *this, true ); + // import all records by using their cached record handle + for( RecordHandleVec::const_iterator aIt = aExtLinkRecs.begin(), aEnd = aExtLinkRecs.end(); (aIt != aEnd) && rStrm.startRecordByHandle( *aIt ); ++aIt ) + aLinkFragment.importRecord( rStrm ); + // finalize global buffers + aLinkFragment.finalizeImport(); + // seek back to the EOF record of the workbook globals fragment + bRet = rStrm.startRecordByHandle( nEofHandle ); + } + + // #i56376# missing EOF - rewind before worksheet BOF record (see above) + if( bRet && isBofRecord( rStrm.getRecId() ) ) + rStrm.rewindRecord(); + + rProgressBar.setPosition( 1.0 ); + return bRet; +} + +bool BiffWorkbookFragment::importSheetFragment( BiffInputStream& rStrm, ISegmentProgressBar& rProgressBar, BiffFragmentType eFragment, sal_Int32 nSheet ) +{ + // find the sheet type for this fragment + WorksheetType eSheetType = SHEETTYPE_WORKSHEET; + bool bSkipSheet = false; + switch( eFragment ) + { + case BIFF_FRAGMENT_WORKSHEET: eSheetType = SHEETTYPE_WORKSHEET; break; + case BIFF_FRAGMENT_CHART: eSheetType = SHEETTYPE_CHART; break; + case BIFF_FRAGMENT_MACRO: eSheetType = SHEETTYPE_MACRO; break; + case BIFF_FRAGMENT_EMPTYSHEET: bSkipSheet = true; break; + default: return false; + } + + // skip this worksheet fragment (e.g. fragment type is BIFF_FRAGMENT_EMPTYSHEET) + if( bSkipSheet ) + { + rProgressBar.setPosition( 1.0 ); + return skipFragment( rStrm ); + } + + /* #i11183# Clear buffers that are used per-sheet, e.g. external links in + BIFF4W and BIFF5 files, or defined names in BIFF4W files. */ + createBuffersPerSheet(); + + // preprocess some records + switch( getBiff() ) + { + // load the workbook globals fragment records in BIFF2-BIFF4 + case BIFF2: + case BIFF3: + case BIFF4: + { + // set sheet index in defined names buffer to handle built-in names correctly + getDefinedNames().setLocalSheetIndex( nSheet ); + // remember current record to seek back below + sal_Int64 nRecHandle = rStrm.getRecHandle(); + // import the global records + ISegmentProgressBarRef xGlobalsProgress = rProgressBar.createSegment( PROGRESS_LENGTH_GLOBALS ); + importGlobalsFragment( rStrm, *xGlobalsProgress ); + // rewind stream to fragment BOF record + rStrm.startRecordByHandle( nRecHandle ); + } + break; + + // load the external link records for this sheet in BIFF5 + case BIFF5: + { + // remember current record to seek back below + sal_Int64 nRecHandle = rStrm.getRecHandle(); + // fragment implementing import of external link records + BiffExternalLinkFragment( *this, false ).importFragment( rStrm ); + // rewind stream to fragment BOF record + rStrm.startRecordByHandle( nRecHandle ); + } + break; + + case BIFF8: + break; + + case BIFF_UNKNOWN: + break; + } + + // create the worksheet fragment + ISegmentProgressBarRef xSheetProgress = rProgressBar.createSegment( rProgressBar.getFreeLength() ); + ::boost::shared_ptr< BiffWorksheetFragmentBase > xFragment; + switch( eSheetType ) + { + case SHEETTYPE_WORKSHEET: + case SHEETTYPE_MACRO: + xFragment.reset( new BiffWorksheetFragment( *this, xSheetProgress, eSheetType, nSheet ) ); + break; + case SHEETTYPE_CHART: + xFragment.reset( new BiffWorksheetFragmentBase( *this, xSheetProgress, eSheetType, nSheet ) ); + break; + } + // load the sheet fragment records + return xFragment->isValidSheet() && xFragment->importFragment( rStrm ); +} + +bool BiffWorkbookFragment::importFilePass( BiffInputStream& rStrm ) +{ + rStrm.enableDecoder( false ); + BiffDecoderRef xDecoder = (getBiff() == BIFF8) ? + lclImportFilePass8( *this, rStrm ) : lclImportFilePass2( *this, rStrm ); + + // set decoder at import stream + rStrm.setDecoder( xDecoder ); + //! TODO remember encryption state for export +// rStrm.GetRoot().GetExtDocOptions().GetDocSettings().mbEncrypted = true; + + return xDecoder.get() && xDecoder->isValid(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/workbookhelper.cxx b/oox/source/xls/workbookhelper.cxx new file mode 100644 index 000000000000..72c599a75437 --- /dev/null +++ b/oox/source/xls/workbookhelper.cxx @@ -0,0 +1,876 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: workbookhelper.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/workbookhelper.hxx" +#include <osl/thread.h> +#include <osl/time.h> +#include <rtl/strbuf.hxx> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/document/XActionLockable.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/sheet/XNamedRanges.hpp> +#include <com/sun/star/sheet/XDatabaseRanges.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include "oox/helper/progressbar.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/core/binaryfilterbase.hxx" +#include "oox/core/xmlfilterbase.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/defnamesbuffer.hxx" +#include "oox/xls/externallinkbuffer.hxx" +#include "oox/xls/formulaparser.hxx" +#include "oox/xls/pagesettings.hxx" +#include "oox/xls/pivottablebuffer.hxx" +#include "oox/xls/sharedstringsbuffer.hxx" +#include "oox/xls/stylesbuffer.hxx" +#include "oox/xls/stylespropertyhelper.hxx" +#include "oox/xls/tablebuffer.hxx" +#include "oox/xls/themebuffer.hxx" +#include "oox/xls/unitconverter.hxx" +#include "oox/xls/validationpropertyhelper.hxx" +#include "oox/xls/viewsettings.hxx" +#include "oox/xls/webquerybuffer.hxx" +#include "oox/xls/workbooksettings.hxx" +#include "oox/xls/worksheetbuffer.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNameContainer; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::awt::XDevice; +using ::com::sun::star::document::XActionLockable; +using ::com::sun::star::sheet::XSpreadsheetDocument; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::sheet::XNamedRanges; +using ::com::sun::star::sheet::XDatabaseRanges; +using ::com::sun::star::style::XStyle; +using ::com::sun::star::style::XStyleFamiliesSupplier; +using ::oox::core::BinaryFilterBase; +using ::oox::core::FilterBase; +using ::oox::core::FragmentHandler; +using ::oox::core::XmlFilterBase; + +// Set this define to 1 to show the load/save time of a document in an assertion. +#define OOX_SHOW_LOADSAVE_TIME 0 + +namespace oox { +namespace xls { + +// ============================================================================ + +#if OSL_DEBUG_LEVEL > 0 + +struct WorkbookDataDebug +{ +#if OOX_SHOW_LOADSAVE_TIME + TimeValue maStartTime; +#endif + sal_Int32 mnDebugCount; + + explicit WorkbookDataDebug(); + ~WorkbookDataDebug(); +}; + +WorkbookDataDebug::WorkbookDataDebug() : + mnDebugCount( 0 ) +{ +#if OOX_SHOW_LOADSAVE_TIME + osl_getSystemTime( &maStartTime ); +#endif +} + +WorkbookDataDebug::~WorkbookDataDebug() +{ +#if OOX_SHOW_LOADSAVE_TIME + TimeValue aEndTime; + osl_getSystemTime( &aEndTime ); + sal_Int32 nMillis = (aEndTime.Seconds - maStartTime.Seconds) * 1000 + static_cast< sal_Int32 >( aEndTime.Nanosec - maStartTime.Nanosec ) / 1000000; + OSL_ENSURE( false, OStringBuffer( "load/save time = " ).append( nMillis / 1000.0 ).append( " seconds" ).getStr() ); +#endif + OSL_ENSURE( mnDebugCount == 0, "WorkbookDataDebug::~WorkbookDataDebug - failed to delete some objects" ); +} + +#endif + +// ============================================================================ + +class WorkbookData +#if OSL_DEBUG_LEVEL > 0 + : public WorkbookDataDebug +#endif +{ +public: + explicit WorkbookData( XmlFilterBase& rFilter ); + explicit WorkbookData( BinaryFilterBase& rFilter, BiffType eBiff ); + ~WorkbookData(); + + /** Returns true, if this helper refers to a valid document. */ + inline bool isValid() const { return mxDoc.is(); } + + // filter ----------------------------------------------------------------- + + /** Returns the base filter object (base class of all filters). */ + inline FilterBase& getBaseFilter() const { return mrBaseFilter; } + /** Returns the filter progress bar. */ + inline SegmentProgressBar& getProgressBar() const { return *mxProgressBar; } + /** Returns the file type of the current filter. */ + inline FilterType getFilterType() const { return meFilterType; } + /** Returns true, if the file is a multi-sheet document, or false if single-sheet. */ + inline bool isWorkbookFile() const { return mbWorkbook; } + + // document model --------------------------------------------------------- + + /** Returns a reference to the source/target spreadsheet document model. */ + inline Reference< XSpreadsheetDocument > getDocument() const { return mxDoc; } + /** Returns a reference to the specified spreadsheet in the document model. */ + Reference< XSpreadsheet > getSheet( sal_Int32 nSheet ) const; + /** Returns the reference device of the document. */ + Reference< XDevice > getReferenceDevice() const; + /** Returns the container for defined names from the Calc document. */ + Reference< XNamedRanges > getNamedRanges() const; + /** Returns the container for database ranges from the Calc document. */ + Reference< XDatabaseRanges > getDatabaseRanges() const; + /** Returns the container for DDE links from the Calc document. */ + Reference< XNameAccess > getDdeLinks() const; + /** Returns the cell or page styles container from the Calc document. */ + Reference< XNameContainer > getStyleFamily( bool bPageStyles ) const; + /** Returns the specified cell or page style from the Calc document. */ + Reference< XStyle > getStyleObject( const OUString& rStyleName, bool bPageStyle ) const; + /** Creates a com.sun.star.style.Style object and returns its final name. */ + Reference< XStyle > createStyleObject( OUString& orStyleName, bool bPageStyle, bool bRenameOldExisting ); + + // buffers ---------------------------------------------------------------- + + /** Returns the global workbook settings object. */ + inline WorkbookSettings& getWorkbookSettings() const { return *mxWorkbookSettings; } + /** Returns the workbook and sheet view settings object. */ + inline ViewSettings& getViewSettings() const { return *mxViewSettings; } + /** Returns the worksheet buffer containing sheet names and properties. */ + inline WorksheetBuffer& getWorksheets() const { return *mxWorksheets; } + /** Returns the office theme object read from the theme substorage. */ + inline ThemeBuffer& getTheme() const { return *mxTheme; } + /** Returns all cell formatting objects read from the styles substream. */ + inline StylesBuffer& getStyles() const { return *mxStyles; } + /** Returns the shared strings read from the shared strings substream. */ + inline SharedStringsBuffer& getSharedStrings() const { return *mxSharedStrings; } + /** Returns the external links read from the external links substream. */ + inline ExternalLinkBuffer& getExternalLinks() const { return *mxExtLinks; } + /** Returns the defined names read from the workbook globals. */ + inline DefinedNamesBuffer& getDefinedNames() const { return *mxDefNames; } + /** Returns the tables collection (equivalent to Calc's database ranges). */ + inline TableBuffer& getTables() const { return *mxTables; } + /** Returns the web queries. */ + inline WebQueryBuffer& getWebQueries() const { return *mxWebQueries; } + /** Returns the pivot tables. */ + inline PivotTableBuffer& getPivotTables() const { return *mxPivotTables; } + + // converters ------------------------------------------------------------- + + /** Returns the import formula parser. */ + inline FormulaParser& getFormulaParser() const { return *mxFmlaParser; } + /** Returns the measurement unit converter. */ + inline UnitConverter& getUnitConverter() const { return *mxUnitConverter; } + /** Returns the converter for string to cell address/range conversion. */ + inline AddressConverter& getAddressConverter() const { return *mxAddrConverter; } + /** Returns the converter for properties related to cell styles. */ + inline StylesPropertyHelper& getStylesPropertyHelper() const { return *mxStylesPropHlp; } + /** Returns the converter for properties related to page/print settings. */ + inline PageSettingsPropertyHelper& getPageSettingsPropertyHelper() const { return *mxPageSettPropHlp; } + /** Returns the converter for properties related to data validation. */ + inline ValidationPropertyHelper& getValidationPropertyHelper() const { return *mxValidationPropHlp; } + + // OOX specific ----------------------------------------------------------- + + /** Returns the base OOX filter object. */ + inline XmlFilterBase& getOoxFilter() const { return *mpOoxFilter; } + + // BIFF specific ---------------------------------------------------------- + + /** Returns the base BIFF filter object. */ + inline BinaryFilterBase& getBiffFilter() const { return *mpBiffFilter; } + /** Returns the BIFF type in binary filter. */ + inline BiffType getBiff() const { return meBiff; } + /** Returns the text encoding used to import/export byte strings. */ + inline rtl_TextEncoding getTextEncoding() const { return meTextEnc; } + /** Sets the text encoding to import/export byte strings. */ + void setTextEncoding( rtl_TextEncoding eTextEnc ); + /** Sets code page read from a CODEPAGE record for byte string import. */ + void setCodePage( sal_uInt16 nCodePage ); + /** Sets text encoding from the default application font, if CODEPAGE record is missing. */ + void setAppFontEncoding( rtl_TextEncoding eAppFontEnc ); + /** Enables workbook file mode, used for BIFF4 workspace files. */ + void setIsWorkbookFile(); + /** Recreates global buffers that are used per sheet in specific BIFF versions. */ + void createBuffersPerSheet(); + /** Looks for a password provided via API, or queries it via GUI. */ + OUString queryPassword(); + +private: + /** Initializes some basic members and sets needed document properties. */ + void initialize(); + /** Finalizes the filter process (sets some needed document properties). */ + void finalize(); + +private: + typedef ::std::auto_ptr< SegmentProgressBar > ProgressBarPtr; + typedef ::std::auto_ptr< WorkbookSettings > WorkbookSettPtr; + typedef ::std::auto_ptr< ViewSettings > ViewSettingsPtr; + typedef ::std::auto_ptr< WorksheetBuffer > WorksheetBfrPtr; + typedef ::std::auto_ptr< ThemeBuffer > ThemeBfrPtr; + typedef ::std::auto_ptr< StylesBuffer > StylesBfrPtr; + typedef ::std::auto_ptr< SharedStringsBuffer > SharedStrBfrPtr; + typedef ::std::auto_ptr< ExternalLinkBuffer > ExtLinkBfrPtr; + typedef ::std::auto_ptr< DefinedNamesBuffer > DefNamesBfrPtr; + typedef ::std::auto_ptr< TableBuffer > TableBfrPtr; + typedef ::std::auto_ptr< WebQueryBuffer > WebQueryBfrPtr; + typedef ::std::auto_ptr< PivotTableBuffer > PivotTableBfrPtr; + typedef ::std::auto_ptr< UnitConverter > UnitConvPtr; + typedef ::std::auto_ptr< AddressConverter > AddressConvPtr; + typedef ::std::auto_ptr< StylesPropertyHelper > StylesPropHlpPtr; + typedef ::std::auto_ptr< PageSettingsPropertyHelper > PageSettPropHlpPtr; + typedef ::std::auto_ptr< ValidationPropertyHelper > ValidationPropHlpPtr; + typedef ::std::auto_ptr< FormulaParser > FormulaParserPtr; + + OUString maRefDeviceProp; /// Property name for reference device. + OUString maNamedRangesProp; /// Property name for defined names. + OUString maDatabaseRangesProp; /// Property name for database ranges. + OUString maDdeLinksProp; /// Property name for DDE links. + OUString maCellStylesProp; /// Property name for cell styles. + OUString maPageStylesProp; /// Property name for page styles. + OUString maCellStyleServ; /// Service name for a cell style. + OUString maPageStyleServ; /// Service name for a page style. + Reference< XSpreadsheetDocument > mxDoc; /// Document model. + FilterBase& mrBaseFilter; /// Base filter object. + FilterType meFilterType; /// File type of the filter. + ProgressBarPtr mxProgressBar; /// The progress bar. + bool mbWorkbook; /// True = multi-sheet file. + + // buffers + WorkbookSettPtr mxWorkbookSettings; /// Global workbook settings. + ViewSettingsPtr mxViewSettings; /// Workbook and sheet view settings. + WorksheetBfrPtr mxWorksheets; /// Sheet info buffer. + ThemeBfrPtr mxTheme; /// Formatting theme from theme substream. + StylesBfrPtr mxStyles; /// All cell style objects from styles substream. + SharedStrBfrPtr mxSharedStrings; /// All strings from shared strings substream. + ExtLinkBfrPtr mxExtLinks; /// All external links. + DefNamesBfrPtr mxDefNames; /// All defined names. + TableBfrPtr mxTables; /// All tables (database ranges). + WebQueryBfrPtr mxWebQueries; /// Web queries buffer. + PivotTableBfrPtr mxPivotTables; /// Pivot tables buffer. + + // converters/helpers + FormulaParserPtr mxFmlaParser; /// Import formula parser. + UnitConvPtr mxUnitConverter; /// General unit converter. + AddressConvPtr mxAddrConverter; /// Cell address and cell range address converter. + StylesPropHlpPtr mxStylesPropHlp; /// Helper for all styles properties. + PageSettPropHlpPtr mxPageSettPropHlp; /// Helper for page/print properties. + ValidationPropHlpPtr mxValidationPropHlp; /// Helper for data validation properties. + + // OOX specific + XmlFilterBase* mpOoxFilter; /// Base OOX filter object. + + // BIFF specific + BinaryFilterBase* mpBiffFilter; /// Base BIFF filter object. + ::rtl::OUString maPassword; /// Password for stream encoder/decoder. + BiffType meBiff; /// BIFF version for BIFF import/export. + rtl_TextEncoding meTextEnc; /// BIFF byte string text encoding. + bool mbHasCodePage; /// True = CODEPAGE record exists in imported stream. + bool mbHasPassword; /// True = password already querried. +}; + +// ---------------------------------------------------------------------------- + +WorkbookData::WorkbookData( XmlFilterBase& rFilter ) : + mrBaseFilter( rFilter ), + meFilterType( FILTER_OOX ), + mpOoxFilter( &rFilter ), + meBiff( BIFF_UNKNOWN ) +{ + initialize(); +} + +WorkbookData::WorkbookData( BinaryFilterBase& rFilter, BiffType eBiff ) : + mrBaseFilter( rFilter ), + meFilterType( FILTER_BIFF ), + mpBiffFilter( &rFilter ), + meBiff( eBiff ) +{ + initialize(); +} + +WorkbookData::~WorkbookData() +{ + finalize(); +} + +// document model ------------------------------------------------------------- + +Reference< XDevice > WorkbookData::getReferenceDevice() const +{ + PropertySet aPropSet( mxDoc ); + Reference< XDevice > xDevice; + aPropSet.getProperty( xDevice, maRefDeviceProp ); + return xDevice; +} + +Reference< XNamedRanges > WorkbookData::getNamedRanges() const +{ + PropertySet aPropSet( mxDoc ); + Reference< XNamedRanges > xNamedRanges; + aPropSet.getProperty( xNamedRanges, maNamedRangesProp ); + return xNamedRanges; +} + +Reference< XDatabaseRanges > WorkbookData::getDatabaseRanges() const +{ + PropertySet aPropSet( mxDoc ); + Reference< XDatabaseRanges > xDatabaseRanges; + aPropSet.getProperty( xDatabaseRanges, maDatabaseRangesProp ); + return xDatabaseRanges; +} + +Reference< XNameAccess > WorkbookData::getDdeLinks() const +{ + PropertySet aPropSet( mxDoc ); + Reference< XNameAccess > xDdeLinks; + aPropSet.getProperty( xDdeLinks, maDdeLinksProp ); + return xDdeLinks; +} + +Reference< XNameContainer > WorkbookData::getStyleFamily( bool bPageStyles ) const +{ + Reference< XNameContainer > xStylesNC; + try + { + Reference< XStyleFamiliesSupplier > xFamiliesSup( mxDoc, UNO_QUERY_THROW ); + Reference< XNameAccess > xFamiliesNA( xFamiliesSup->getStyleFamilies(), UNO_QUERY_THROW ); + xStylesNC.set( xFamiliesNA->getByName( bPageStyles ? maPageStylesProp : maCellStylesProp ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + } + OSL_ENSURE( xStylesNC.is(), "WorkbookData::getStyleFamily - cannot access style family" ); + return xStylesNC; +} + +Reference< XStyle > WorkbookData::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const +{ + Reference< XStyle > xStyle; + Reference< XNameContainer > xStylesNC = getStyleFamily( bPageStyle ); + if( xStylesNC.is() ) try + { + xStyle.set( xStylesNC->getByName( rStyleName ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + } + OSL_ENSURE( xStyle.is(), "WorkbookData::getStyleObject - cannot access style object" ); + return xStyle; +} + +Reference< XStyle > WorkbookData::createStyleObject( OUString& orStyleName, bool bPageStyle, bool bRenameOldExisting ) +{ + Reference< XStyle > xStyle; + Reference< XNameContainer > xStylesNC = getStyleFamily( bPageStyle ); + if( xStylesNC.is() ) try + { + Reference< XMultiServiceFactory > xFactory( mxDoc, UNO_QUERY_THROW ); + xStyle.set( xFactory->createInstance( bPageStyle ? maPageStyleServ : maCellStyleServ ), UNO_QUERY_THROW ); + orStyleName = ContainerHelper::insertByUnusedName( xStylesNC, Any( xStyle ), orStyleName, ' ', bRenameOldExisting ); + } + catch( Exception& ) + { + } + OSL_ENSURE( xStyle.is(), "WorkbookData::createStyleObject - cannot create style" ); + return xStyle; +} + +// BIFF specific -------------------------------------------------------------- + +void WorkbookData::setTextEncoding( rtl_TextEncoding eTextEnc ) +{ + if( eTextEnc != RTL_TEXTENCODING_DONTKNOW ) + meTextEnc = eTextEnc; +} + +void WorkbookData::setCodePage( sal_uInt16 nCodePage ) +{ + setTextEncoding( BiffHelper::calcTextEncodingFromCodePage( nCodePage ) ); + mbHasCodePage = true; +} + +void WorkbookData::setAppFontEncoding( rtl_TextEncoding eAppFontEnc ) +{ + if( !mbHasCodePage ) + setTextEncoding( eAppFontEnc ); +} + +void WorkbookData::setIsWorkbookFile() +{ + OSL_ENSURE( meBiff == BIFF4, "WorkbookData::setIsWorkbookFile - invalid call" ); + mbWorkbook = true; +} + +void WorkbookData::createBuffersPerSheet() +{ + switch( meBiff ) + { + case BIFF2: + case BIFF3: + break; + + case BIFF4: + // #i11183# sheets in BIFF4W files have own styles or names + if( mbWorkbook ) + { + mxStyles.reset( new StylesBuffer( WorkbookHelper( *this ) ) ); + mxDefNames.reset( new DefinedNamesBuffer( WorkbookHelper( *this ) ) ); + mxExtLinks.reset( new ExternalLinkBuffer( WorkbookHelper( *this ) ) ); + } + break; + + case BIFF5: + // BIFF5 stores external references per sheet + mxExtLinks.reset( new ExternalLinkBuffer( WorkbookHelper( *this ) ) ); + break; + + case BIFF8: + break; + + case BIFF_UNKNOWN: + break; + } +} + +OUString WorkbookData::queryPassword() +{ + if( !mbHasPassword ) + { + //! TODO + maPassword = OUString(); + // set to true, even if dialog has been cancelled (never ask twice) + mbHasPassword = true; + } + return maPassword; +} + +// private -------------------------------------------------------------------- + +void WorkbookData::initialize() +{ + maRefDeviceProp = CREATE_OUSTRING( "ReferenceDevice" ); + maNamedRangesProp = CREATE_OUSTRING( "NamedRanges" ); + maDatabaseRangesProp = CREATE_OUSTRING( "DatabaseRanges" ); + maDdeLinksProp = CREATE_OUSTRING( "DDELinks" ); + maCellStylesProp = CREATE_OUSTRING( "CellStyles" ); + maPageStylesProp = CREATE_OUSTRING( "PageStyles" ); + maCellStyleServ = CREATE_OUSTRING( "com.sun.star.style.CellStyle" ); + maPageStyleServ = CREATE_OUSTRING( "com.sun.star.style.PageStyle" ); + mbWorkbook = false; + meTextEnc = osl_getThreadTextEncoding(); + mbHasCodePage = false; + mbHasPassword = false; + + // the spreadsheet document + mxDoc.set( mrBaseFilter.getModel(), UNO_QUERY ); + OSL_ENSURE( mxDoc.is(), "WorkbookData::initialize - no spreadsheet document" ); + + mxWorkbookSettings.reset( new WorkbookSettings( WorkbookHelper( *this ) ) ); + mxViewSettings.reset( new ViewSettings( WorkbookHelper( *this ) ) ); + mxWorksheets.reset( new WorksheetBuffer( WorkbookHelper( *this ) ) ); + mxTheme.reset( new ThemeBuffer( WorkbookHelper( *this ) ) ); + mxStyles.reset( new StylesBuffer( WorkbookHelper( *this ) ) ); + mxSharedStrings.reset( new SharedStringsBuffer( WorkbookHelper( *this ) ) ); + mxExtLinks.reset( new ExternalLinkBuffer( WorkbookHelper( *this ) ) ); + mxDefNames.reset( new DefinedNamesBuffer( WorkbookHelper( *this ) ) ); + mxTables.reset( new TableBuffer( WorkbookHelper( *this ) ) ); + mxWebQueries.reset( new WebQueryBuffer( WorkbookHelper( *this ) ) ); + mxPivotTables.reset( new PivotTableBuffer( WorkbookHelper( *this ) ) ); + + mxUnitConverter.reset( new UnitConverter( WorkbookHelper( *this ) ) ); + mxAddrConverter.reset( new AddressConverter( WorkbookHelper( *this ) ) ); + mxStylesPropHlp.reset( new StylesPropertyHelper( WorkbookHelper( *this ) ) ); + mxPageSettPropHlp.reset( new PageSettingsPropertyHelper( WorkbookHelper( *this ) ) ); + mxValidationPropHlp.reset( new ValidationPropertyHelper( WorkbookHelper( *this ) ) ); + + // set some document properties needed during import + if( mrBaseFilter.isImportFilter() ) + { + PropertySet aPropSet( mxDoc ); + // enable editing read-only documents (e.g. from read-only files) + aPropSet.setProperty( CREATE_OUSTRING( "IsChangeReadOnlyEnabled" ), true ); + // #i76026# disable Undo while loading the document + aPropSet.setProperty( CREATE_OUSTRING( "IsUndoEnabled" ), false ); + // #i79826# disable calculating automatic row height while loading the document + aPropSet.setProperty( CREATE_OUSTRING( "IsAdjustHeightEnabled" ), false ); + // disable automatic update of linked sheets and DDE links + aPropSet.setProperty( CREATE_OUSTRING( "IsExecuteLinkEnabled" ), false ); + // #i79890# disable automatic update of defined names + Reference< XActionLockable > xLockable( getNamedRanges(), UNO_QUERY ); + if( xLockable.is() ) + xLockable->addActionLock(); + + //! TODO: localize progress bar text + mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), CREATE_OUSTRING( "Loading..." ) ) ); + mxFmlaParser.reset( new FormulaParser( WorkbookHelper( *this ) ) ); + } + else if( mrBaseFilter.isExportFilter() ) + { + //! TODO: localize progress bar text + mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), CREATE_OUSTRING( "Saving..." ) ) ); + } +} + +void WorkbookData::finalize() +{ + // set some document properties needed after import + if( mrBaseFilter.isImportFilter() ) + { + PropertySet aPropSet( mxDoc ); + // #i74668# do not insert default sheets + aPropSet.setProperty( CREATE_OUSTRING( "IsLoaded" ), true ); + // #i79890# enable automatic update of defined names (before IsAdjustHeightEnabled!) + Reference< XActionLockable > xLockable( getNamedRanges(), UNO_QUERY ); + if( xLockable.is() ) + xLockable->removeActionLock(); + // enable automatic update of linked sheets and DDE links + aPropSet.setProperty( CREATE_OUSTRING( "IsExecuteLinkEnabled" ), true ); + // #i79826# enable updating automatic row height after loading the document + aPropSet.setProperty( CREATE_OUSTRING( "IsAdjustHeightEnabled" ), true ); + // #i76026# enable Undo after loading the document + aPropSet.setProperty( CREATE_OUSTRING( "IsUndoEnabled" ), true ); + // disable editing read-only documents (e.g. from read-only files) + aPropSet.setProperty( CREATE_OUSTRING( "IsChangeReadOnlyEnabled" ), false ); + } +} + +// ============================================================================ + +WorkbookHelper::WorkbookHelper( WorkbookData& rBookData ) : +#if OSL_DEBUG_LEVEL > 0 + WorkbookHelperDebug( rBookData.mnDebugCount ), +#endif + mrBookData( rBookData ) +{ +} + +WorkbookHelper::~WorkbookHelper() +{ +} + +// filter --------------------------------------------------------------------- + +FilterBase& WorkbookHelper::getBaseFilter() const +{ + return mrBookData.getBaseFilter(); +} + +FilterType WorkbookHelper::getFilterType() const +{ + return mrBookData.getFilterType(); +} + +SegmentProgressBar& WorkbookHelper::getProgressBar() const +{ + return mrBookData.getProgressBar(); +} + +bool WorkbookHelper::isWorkbookFile() const +{ + return mrBookData.isWorkbookFile(); +} + +void WorkbookHelper::finalizeWorkbookImport() +{ + // workbook settings, document and sheet view settings + mrBookData.getWorkbookSettings().finalizeImport(); + mrBookData.getViewSettings().finalizeImport(); +} + +// document model ------------------------------------------------------------- + +Reference< XSpreadsheetDocument > WorkbookHelper::getDocument() const +{ + return mrBookData.getDocument(); +} + +Reference< XSpreadsheet > WorkbookHelper::getSheet( sal_Int32 nSheet ) const +{ + Reference< XSpreadsheet > xSheet; + try + { + Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW ); + xSheet.set( xSheetsIA->getByIndex( nSheet ), UNO_QUERY_THROW ); + } + catch( Exception& ) + { + } + return xSheet; +} + +Reference< XDevice > WorkbookHelper::getReferenceDevice() const +{ + return mrBookData.getReferenceDevice(); +} + +Reference< XNamedRanges > WorkbookHelper::getNamedRanges() const +{ + return mrBookData.getNamedRanges(); +} + +Reference< XDatabaseRanges > WorkbookHelper::getDatabaseRanges() const +{ + return mrBookData.getDatabaseRanges(); +} + +Reference< XNameAccess > WorkbookHelper::getDdeLinks() const +{ + return mrBookData.getDdeLinks(); +} + +Reference< XNameContainer > WorkbookHelper::getStyleFamily( bool bPageStyles ) const +{ + return mrBookData.getStyleFamily( bPageStyles ); +} + +Reference< XStyle > WorkbookHelper::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const +{ + return mrBookData.getStyleObject( rStyleName, bPageStyle ); +} + +Reference< XStyle > WorkbookHelper::createStyleObject( OUString& orStyleName, bool bPageStyle, bool bRenameOldExisting ) +{ + return mrBookData.createStyleObject( orStyleName, bPageStyle, bRenameOldExisting ); +} + +// buffers -------------------------------------------------------------------- + +WorkbookSettings& WorkbookHelper::getWorkbookSettings() const +{ + return mrBookData.getWorkbookSettings(); +} + +ViewSettings& WorkbookHelper::getViewSettings() const +{ + return mrBookData.getViewSettings(); +} + +WorksheetBuffer& WorkbookHelper::getWorksheets() const +{ + return mrBookData.getWorksheets(); +} + +ThemeBuffer& WorkbookHelper::getTheme() const +{ + return mrBookData.getTheme(); +} + +StylesBuffer& WorkbookHelper::getStyles() const +{ + return mrBookData.getStyles(); +} + +SharedStringsBuffer& WorkbookHelper::getSharedStrings() const +{ + return mrBookData.getSharedStrings(); +} + +ExternalLinkBuffer& WorkbookHelper::getExternalLinks() const +{ + return mrBookData.getExternalLinks(); +} + +DefinedNamesBuffer& WorkbookHelper::getDefinedNames() const +{ + return mrBookData.getDefinedNames(); +} + +TableBuffer& WorkbookHelper::getTables() const +{ + return mrBookData.getTables(); +} + +WebQueryBuffer& WorkbookHelper::getWebQueries() const +{ + return mrBookData.getWebQueries(); +} + +PivotTableBuffer& WorkbookHelper::getPivotTables() const +{ + return mrBookData.getPivotTables(); +} + +// converters ----------------------------------------------------------------- + +FormulaParser& WorkbookHelper::getFormulaParser() const +{ + return mrBookData.getFormulaParser(); +} + +UnitConverter& WorkbookHelper::getUnitConverter() const +{ + return mrBookData.getUnitConverter(); +} + +AddressConverter& WorkbookHelper::getAddressConverter() const +{ + return mrBookData.getAddressConverter(); +} + +StylesPropertyHelper& WorkbookHelper::getStylesPropertyHelper() const +{ + return mrBookData.getStylesPropertyHelper(); +} + +PageSettingsPropertyHelper& WorkbookHelper::getPageSettingsPropertyHelper() const +{ + return mrBookData.getPageSettingsPropertyHelper(); +} + +ValidationPropertyHelper& WorkbookHelper::getValidationPropertyHelper() const +{ + return mrBookData.getValidationPropertyHelper(); +} + +// OOX specific --------------------------------------------------------------- + +XmlFilterBase& WorkbookHelper::getOoxFilter() const +{ + OSL_ENSURE( mrBookData.getFilterType() == FILTER_OOX, "WorkbookHelper::getOoxFilter - invalid call" ); + return mrBookData.getOoxFilter(); +} + +bool WorkbookHelper::importOoxFragment( const ::rtl::Reference< FragmentHandler >& rxHandler ) +{ + return getOoxFilter().importFragment( rxHandler ); +} + +// BIFF specific -------------------------------------------------------------- + +BinaryFilterBase& WorkbookHelper::getBiffFilter() const +{ + OSL_ENSURE( mrBookData.getFilterType() == FILTER_BIFF, "WorkbookHelper::getBiffFilter - invalid call" ); + return mrBookData.getBiffFilter(); +} + +BiffType WorkbookHelper::getBiff() const +{ + return mrBookData.getBiff(); +} + +rtl_TextEncoding WorkbookHelper::getTextEncoding() const +{ + return mrBookData.getTextEncoding(); +} + +void WorkbookHelper::setTextEncoding( rtl_TextEncoding eTextEnc ) +{ + mrBookData.setTextEncoding( eTextEnc ); +} + +void WorkbookHelper::setCodePage( sal_uInt16 nCodePage ) +{ + mrBookData.setCodePage( nCodePage ); +} + +void WorkbookHelper::setAppFontEncoding( rtl_TextEncoding eAppFontEnc ) +{ + mrBookData.setAppFontEncoding( eAppFontEnc ); +} + +void WorkbookHelper::setIsWorkbookFile() +{ + mrBookData.setIsWorkbookFile(); +} + +void WorkbookHelper::createBuffersPerSheet() +{ + mrBookData.createBuffersPerSheet(); +} + +OUString WorkbookHelper::queryPassword() const +{ + return mrBookData.queryPassword(); +} + +// ============================================================================ + +namespace prv { + +WorkbookDataOwner::WorkbookDataOwner( WorkbookDataRef xBookData ) : + mxBookData( xBookData ) +{ +} + +WorkbookDataOwner::~WorkbookDataOwner() +{ +} + +} // namespace prv + +// ---------------------------------------------------------------------------- + +WorkbookHelperRoot::WorkbookHelperRoot( ::oox::core::XmlFilterBase& rFilter ) : + prv::WorkbookDataOwner( prv::WorkbookDataRef( new WorkbookData( rFilter ) ) ), + WorkbookHelper( *mxBookData ) +{ +} + +WorkbookHelperRoot::WorkbookHelperRoot( ::oox::core::BinaryFilterBase& rFilter, BiffType eBiff ) : + prv::WorkbookDataOwner( prv::WorkbookDataRef( new WorkbookData( rFilter, eBiff ) ) ), + WorkbookHelper( *mxBookData ) +{ +} + +bool WorkbookHelperRoot::isValid() const +{ + return mxBookData->isValid(); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/workbooksettings.cxx b/oox/source/xls/workbooksettings.cxx new file mode 100644 index 000000000000..bb758dbb9511 --- /dev/null +++ b/oox/source/xls/workbooksettings.cxx @@ -0,0 +1,297 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: workbooksettings.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/workbooksettings.hxx" +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/sheet/XCalculatable.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::util::Date; +using ::com::sun::star::util::XNumberFormatsSupplier; +using ::com::sun::star::sheet::XCalculatable; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt32 OOBIN_WORKBOOKPR_DATE1904 = 0x00000001; +const sal_uInt32 OOBIN_WORKBOOKPR_STRIPEXT = 0x00000080; + +const sal_uInt16 OOBIN_CALCPR_A1 = 0x0002; +const sal_uInt16 OOBIN_CALCPR_ITERATE = 0x0004; +const sal_uInt16 OOBIN_CALCPR_FULLPRECISION = 0x0008; +const sal_uInt16 OOBIN_CALCPR_CALCCOMPLETED = 0x0010; +const sal_uInt16 OOBIN_CALCPR_CALCONSAVE = 0x0020; +const sal_uInt16 OOBIN_CALCPR_CONCURRENT = 0x0040; +const sal_uInt16 OOBIN_CALCPR_MANUALPROC = 0x0080; + +// no predefined constants for show objects mode +const sal_Int16 API_SHOWMODE_SHOW = 0; /// Show drawing objects. +const sal_Int16 API_SHOWMODE_HIDE = 1; /// Hide drawing objects. +const sal_Int16 API_SHOWMODE_PLACEHOLDER = 2; /// Show placeholders for drawing objects. + +} // namespace + +// ============================================================================ + +OoxWorkbookPrData::OoxWorkbookPrData() : + mnShowObjectMode( XML_all ), + mnUpdateLinksMode( XML_userSet ), + mnDefaultThemeVer( -1 ), + mbDateMode1904( false ), + mbSaveExtLinkValues( true ) +{ +} + +void OoxWorkbookPrData::setBinObjectMode( sal_uInt16 nObjMode ) +{ + static const sal_Int32 spnObjModes[] = { XML_all, XML_placeholders, XML_none }; + mnShowObjectMode = STATIC_ARRAY_SELECT( spnObjModes, nObjMode, XML_all ); +} + +// ============================================================================ + +OoxCalcPrData::OoxCalcPrData() : + mfIterateDelta( 0.001 ), + mnCalcId( -1 ), + mnRefMode( XML_A1 ), + mnCalcMode( XML_auto ), + mnIterateCount( 100 ), + mnProcCount( -1 ), + mbCalcOnSave( true ), + mbCalcCompleted( true ), + mbFullPrecision( true ), + mbIterate( false ), + mbConcurrent( true ), + mbUseNlr( false ) +{ +} + +// ============================================================================ + +WorkbookSettings::WorkbookSettings( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ) +{ +} + +void WorkbookSettings::importWorkbookPr( const AttributeList& rAttribs ) +{ + maOoxBookData.maCodeName = rAttribs.getString( XML_codePage ); + maOoxBookData.mnShowObjectMode = rAttribs.getToken( XML_showObjects, XML_all ); + maOoxBookData.mnUpdateLinksMode = rAttribs.getToken( XML_updateLinks, XML_userSet ); + maOoxBookData.mnDefaultThemeVer = rAttribs.getInteger( XML_defaultThemeVersion, -1 ); + maOoxBookData.mbDateMode1904 = rAttribs.getBool( XML_date1904, false ); + maOoxBookData.mbSaveExtLinkValues = rAttribs.getBool( XML_saveExternalLinkValues, true ); +} + +void WorkbookSettings::importCalcPr( const AttributeList& rAttribs ) +{ + maOoxCalcData.mfIterateDelta = rAttribs.getDouble( XML_iterateDelta, 0.0001 ); + maOoxCalcData.mnCalcId = rAttribs.getInteger( XML_calcId, -1 ); + maOoxCalcData.mnRefMode = rAttribs.getToken( XML_refMode, XML_A1 ); + maOoxCalcData.mnCalcMode = rAttribs.getToken( XML_calcMode, XML_auto ); + maOoxCalcData.mnIterateCount = rAttribs.getInteger( XML_iterateCount, 100 ); + maOoxCalcData.mnProcCount = rAttribs.getInteger( XML_concurrentManualCount, -1 ); + maOoxCalcData.mbCalcOnSave = rAttribs.getBool( XML_calcOnSave, true ); + maOoxCalcData.mbCalcCompleted = rAttribs.getBool( XML_calcCompleted, true ); + maOoxCalcData.mbFullPrecision = rAttribs.getBool( XML_fullPrecision, true ); + maOoxCalcData.mbIterate = rAttribs.getBool( XML_iterate, false ); + maOoxCalcData.mbConcurrent = rAttribs.getBool( XML_concurrentCalc, true ); +} + +void WorkbookSettings::importWorkbookPr( RecordInputStream& rStrm ) +{ + sal_uInt32 nFlags; + rStrm >> nFlags >> maOoxBookData.mnDefaultThemeVer >> maOoxBookData.maCodeName; + maOoxBookData.setBinObjectMode( extractValue< sal_uInt16 >( nFlags, 13, 2 ) ); + maOoxBookData.mbDateMode1904 = getFlag( nFlags, OOBIN_WORKBOOKPR_DATE1904 ); + // set flag means: strip external link values + maOoxBookData.mbSaveExtLinkValues = !getFlag( nFlags, OOBIN_WORKBOOKPR_STRIPEXT ); +} + +void WorkbookSettings::importCalcPr( RecordInputStream& rStrm ) +{ + sal_Int32 nCalcMode, nProcCount; + sal_uInt16 nFlags; + rStrm >> maOoxCalcData.mnCalcId >> nCalcMode >> maOoxCalcData.mnIterateCount >> maOoxCalcData.mfIterateDelta >> nProcCount >> nFlags; + + static const sal_Int32 spnCalcModes[] = { XML_manual, XML_auto, XML_autoNoTable }; + maOoxCalcData.mnRefMode = getFlagValue( nFlags, OOBIN_CALCPR_A1, XML_A1, XML_R1C1 ); + maOoxCalcData.mnCalcMode = STATIC_ARRAY_SELECT( spnCalcModes, nCalcMode, XML_auto ); + maOoxCalcData.mnProcCount = getFlagValue< sal_Int32 >( nFlags, OOBIN_CALCPR_MANUALPROC, nProcCount, -1 ); + maOoxCalcData.mbCalcOnSave = getFlag( nFlags, OOBIN_CALCPR_CALCONSAVE ); + maOoxCalcData.mbCalcCompleted = getFlag( nFlags, OOBIN_CALCPR_CALCCOMPLETED ); + maOoxCalcData.mbFullPrecision = getFlag( nFlags, OOBIN_CALCPR_FULLPRECISION ); + maOoxCalcData.mbIterate = getFlag( nFlags, OOBIN_CALCPR_ITERATE ); + maOoxCalcData.mbConcurrent = getFlag( nFlags, OOBIN_CALCPR_CONCURRENT ); +} + +void WorkbookSettings::setSaveExtLinkValues( bool bSaveExtLinks ) +{ + maOoxBookData.mbSaveExtLinkValues = bSaveExtLinks; +} + +void WorkbookSettings::importBookBool( BiffInputStream& rStrm ) +{ + // value of 0 means save external values, value of 1 means strip external values + maOoxBookData.mbSaveExtLinkValues = rStrm.readuInt16() == 0; +} + +void WorkbookSettings::importCalcCount( BiffInputStream& rStrm ) +{ + maOoxCalcData.mnIterateCount = rStrm.readuInt16(); +} + +void WorkbookSettings::importCalcMode( BiffInputStream& rStrm ) +{ + sal_Int16 nCalcMode = rStrm.readInt16() + 1; + static const sal_Int32 spnCalcModes[] = { XML_autoNoTable, XML_manual, XML_auto }; + maOoxCalcData.mnCalcMode = STATIC_ARRAY_SELECT( spnCalcModes, nCalcMode, XML_auto ); +} + +void WorkbookSettings::importCodeName( BiffInputStream& rStrm ) +{ + maOoxBookData.maCodeName = rStrm.readUniString(); +} + +void WorkbookSettings::importDateMode( BiffInputStream& rStrm ) +{ + maOoxBookData.mbDateMode1904 = rStrm.readuInt16() != 0; +} + +void WorkbookSettings::importDelta( BiffInputStream& rStrm ) +{ + rStrm >> maOoxCalcData.mfIterateDelta; +} + +void WorkbookSettings::importHideObj( BiffInputStream& rStrm ) +{ + maOoxBookData.setBinObjectMode( rStrm.readuInt16() ); +} + +void WorkbookSettings::importIteration( BiffInputStream& rStrm ) +{ + maOoxCalcData.mbIterate = rStrm.readuInt16() != 0; +} + +void WorkbookSettings::importPrecision( BiffInputStream& rStrm ) +{ + maOoxCalcData.mbFullPrecision = rStrm.readuInt16() != 0; +} + +void WorkbookSettings::importRefMode( BiffInputStream& rStrm ) +{ + maOoxCalcData.mnRefMode = (rStrm.readuInt16() == 0) ? XML_R1C1 : XML_A1; +} + +void WorkbookSettings::importSaveRecalc( BiffInputStream& rStrm ) +{ + maOoxCalcData.mbCalcOnSave = rStrm.readuInt16() != 0; +} + +void WorkbookSettings::importUncalced( BiffInputStream& ) +{ + // existence of this record indicates incomplete recalc + maOoxCalcData.mbCalcCompleted = false; +} + +void WorkbookSettings::importUsesElfs( BiffInputStream& rStrm ) +{ + maOoxCalcData.mbUseNlr = rStrm.readuInt16() != 0; +} + +void WorkbookSettings::finalizeImport() +{ + // default settings + PropertySet aPropSet( getDocument() ); + switch( getFilterType() ) + { + case FILTER_OOX: + case FILTER_BIFF: + aPropSet.setProperty( CREATE_OUSTRING( "IgnoreCase" ), true ); // always in Excel + aPropSet.setProperty( CREATE_OUSTRING( "RegularExpressions" ), false ); // not supported in Excel + break; + case FILTER_UNKNOWN: + break; + } + + // calculation settings + Date aNullDate = maOoxBookData.mbDateMode1904 ? Date( 1, 1, 1904 ) : Date( 30, 12, 1899 ); + + aPropSet.setProperty( CREATE_OUSTRING( "NullDate" ), aNullDate ); + aPropSet.setProperty( CREATE_OUSTRING( "IsIterationEnabled" ), maOoxCalcData.mbIterate ); + aPropSet.setProperty( CREATE_OUSTRING( "IterationCount" ), maOoxCalcData.mnIterateCount ); + aPropSet.setProperty( CREATE_OUSTRING( "IterationEpsilon" ), maOoxCalcData.mfIterateDelta ); + aPropSet.setProperty( CREATE_OUSTRING( "CalcAsShown" ), !maOoxCalcData.mbFullPrecision ); + aPropSet.setProperty( CREATE_OUSTRING( "LookUpLabels" ), maOoxCalcData.mbUseNlr ); + + Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY ); + if( xNumFmtsSupp.is() ) + { + PropertySet aNumFmtProp( xNumFmtsSupp->getNumberFormatSettings() ); + aNumFmtProp.setProperty( CREATE_OUSTRING( "NullDate" ), aNullDate ); + } + + Reference< XCalculatable > xCalculatable( getDocument(), UNO_QUERY ); + if( xCalculatable.is() ) + xCalculatable->enableAutomaticCalculation( (maOoxCalcData.mnCalcMode == XML_auto) || (maOoxCalcData.mnCalcMode == XML_autoNoTable) ); +} + +sal_Int16 WorkbookSettings::getApiShowObjectMode() const +{ + switch( maOoxBookData.mnShowObjectMode ) + { + case XML_all: return API_SHOWMODE_SHOW; + case XML_none: return API_SHOWMODE_HIDE; + // #i80528# placeholders not supported anymore, but this is handled internally in Calc + case XML_placeholders: return API_SHOWMODE_PLACEHOLDER; + } + return API_SHOWMODE_SHOW; +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/worksheetbuffer.cxx b/oox/source/xls/worksheetbuffer.cxx new file mode 100644 index 000000000000..d52d75bab6b0 --- /dev/null +++ b/oox/source/xls/worksheetbuffer.cxx @@ -0,0 +1,333 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: worksheetbuffer.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/worksheetbuffer.hxx" +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/i18n/XCharacterClassification.hpp> +#include <com/sun/star/i18n/KParseTokens.hpp> +#include <com/sun/star/i18n/KParseType.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XExternalSheetName.hpp> +#include <com/sun/star/sheet/XSheetLinkable.hpp> +#include <comphelper/processfactory.hxx> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/containerhelper.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/core/filterbase.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/ooxtokens.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNamed; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::i18n::ParseResult; +using ::com::sun::star::sheet::XSpreadsheetDocument; +using ::com::sun::star::sheet::XSpreadsheets; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::sheet::XExternalSheetName; +using ::com::sun::star::sheet::XSheetLinkable; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +/** Returns the base file name without path and extension. */ +OUString lclGetBaseFileName( const OUString& rUrl ) +{ + sal_Int32 nFileNamePos = ::std::max< sal_Int32 >( rUrl.lastIndexOf( '/' ) + 1, 0 ); + sal_Int32 nExtPos = rUrl.lastIndexOf( '.' ); + if( nExtPos <= nFileNamePos ) nExtPos = rUrl.getLength(); + return rUrl.copy( nFileNamePos, nExtPos - nFileNamePos ); +} + +} // namespace + +// ============================================================================ + +OoxSheetInfo::OoxSheetInfo() : + mnSheetId( -1 ), + mnState( XML_visible ) +{ +} + +// ============================================================================ + +WorksheetBuffer::WorksheetBuffer( const WorkbookHelper& rHelper ) : + WorkbookHelper( rHelper ), + maIsVisibleProp( CREATE_OUSTRING( "IsVisible" ) ) +{ + // character classification service for conversion to valid sheet names + Reference< XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory(); + mxCharClass.set( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.i18n.CharacterClassification" ) ), UNO_QUERY ); + OSL_ENSURE( mxCharClass.is(), "WorksheetBuffer::WorksheetBuffer - no character classification service" ); +} + +void WorksheetBuffer::initializeSingleSheet() +{ + OSL_ENSURE( maSheetInfos.empty(), "WorksheetBuffer::initializeSingleSheet - invalid call" ); + OoxSheetInfo aSheetInfo; + aSheetInfo.maName = lclGetBaseFileName( getBaseFilter().getFileUrl() ); + insertSheet( aSheetInfo ); +} + +void WorksheetBuffer::importSheet( const AttributeList& rAttribs ) +{ + OoxSheetInfo aSheetInfo; + aSheetInfo.maId = rAttribs.getString( R_TOKEN( id ) ); + aSheetInfo.maName = rAttribs.getString( XML_name ); + aSheetInfo.mnSheetId = rAttribs.getInteger( XML_sheetId, -1 ); + aSheetInfo.mnState = rAttribs.getToken( XML_state, XML_visible ); + insertSheet( aSheetInfo ); +} + +void WorksheetBuffer::importSheet( RecordInputStream& rStrm ) +{ + sal_Int32 nState; + OoxSheetInfo aSheetInfo; + rStrm >> nState >> aSheetInfo.mnSheetId >> aSheetInfo.maId >> aSheetInfo.maName; + static const sal_Int32 spnStates[] = { XML_visible, XML_hidden, XML_veryHidden }; + aSheetInfo.mnState = STATIC_ARRAY_SELECT( spnStates, nState, XML_visible ); + insertSheet( aSheetInfo ); +} + +void WorksheetBuffer::importSheet( BiffInputStream& rStrm ) +{ + sal_uInt16 nState = 0; + if( getBiff() >= BIFF5 ) + { + rStrm.skip( 4 ); + rStrm >> nState; + } + + OoxSheetInfo aSheetInfo; + aSheetInfo.maName = (getBiff() == BIFF8) ? + rStrm.readUniString( rStrm.readuInt8() ) : + rStrm.readByteString( false, getTextEncoding() ); + static const sal_Int32 spnStates[] = { XML_visible, XML_hidden, XML_veryHidden }; + aSheetInfo.mnState = STATIC_ARRAY_SELECT( spnStates, nState, XML_visible ); + insertSheet( aSheetInfo ); +} + +sal_Int32 WorksheetBuffer::insertExternalSheet( const OUString& rTargetUrl, const OUString& rSheetName ) +{ + // try to find existing external sheet (needed for BIFF4W and BIFF5) + ExternalSheetName aExtSheet( rTargetUrl, rSheetName ); + ExternalSheetMap::iterator aIt = maExternalSheets.find( aExtSheet ); + if( aIt != maExternalSheets.end() ) + return aIt->second; + + // create a new external sheet + sal_Int16& rnSheet = maExternalSheets[ aExtSheet ]; + rnSheet = getTotalSheetCount(); + insertSheet( OUString(), rnSheet, false ); + + try + { + Reference< XSpreadsheet > xSheet = getSheet( rnSheet ); + // use base file name as sheet name, if sheet name is missing (e.g. links to BIFF2-BIFF4 files) + OUString aSheetName = rSheetName; + if( aSheetName.getLength() == 0 ) + aSheetName = lclGetBaseFileName( rTargetUrl ); + // link the sheet + Reference< XSheetLinkable > xLinkable( xSheet, UNO_QUERY_THROW ); + xLinkable->link( rTargetUrl, aSheetName, OUString(), OUString(), ::com::sun::star::sheet::SheetLinkMode_VALUE ); + // set the special external sheet name + Reference< XExternalSheetName > xSheetName( xSheet, UNO_QUERY_THROW ); + xSheetName->setExternalName( xLinkable->getLinkUrl(), xLinkable->getLinkSheetName() ); + } + catch( Exception& ) + { + } + + // return sheet index + return rnSheet; +} + +sal_Int32 WorksheetBuffer::getInternalSheetCount() const +{ + return static_cast< sal_Int32 >( maSheetInfos.size() ); +} + +OUString WorksheetBuffer::getSheetRelId( sal_Int32 nSheet ) const +{ + OUString aRelId; + if( const OoxSheetInfo* pInfo = getSheetInfo( nSheet ) ) + aRelId = pInfo->maId; + return aRelId; +} + +OUString WorksheetBuffer::getFinalSheetName( sal_Int32 nSheet ) const +{ + OUString aName; + if( const OoxSheetInfo* pInfo = getSheetInfo( nSheet ) ) + aName = pInfo->maFinalName; + return aName; +} + +OUString WorksheetBuffer::getFinalSheetName( const OUString& rName ) const +{ + for( SheetInfoVec::const_iterator aIt = maSheetInfos.begin(), aEnd = maSheetInfos.end(); aIt != aEnd; ++aIt ) + // TODO: handle encoded characters + if( aIt->maName.equalsIgnoreAsciiCase( rName ) ) + return aIt->maFinalName; + return OUString(); +} + +sal_Int32 WorksheetBuffer::getFinalSheetIndex( const OUString& rName ) const +{ + for( SheetInfoVec::const_iterator aIt = maSheetInfos.begin(), aEnd = maSheetInfos.end(); aIt != aEnd; ++aIt ) + // TODO: handle encoded characters + if( aIt->maName.equalsIgnoreAsciiCase( rName ) ) + return static_cast< sal_Int32 >( aIt - maSheetInfos.begin() ); + return -1; +} + +// private -------------------------------------------------------------------- + +sal_Int16 WorksheetBuffer::getTotalSheetCount() const +{ + try + { + Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW ); + return static_cast< sal_Int16 >( xSheetsIA->getCount() ); + } + catch( Exception& ) + { + OSL_ENSURE( false, "WorksheetBuffer::getTotalSheetCount - cannot get sheet count" ); + } + return 1; +} + +const OoxSheetInfo* WorksheetBuffer::getSheetInfo( sal_Int32 nSheet ) const +{ + return ((0 <= nSheet) && (static_cast< size_t >( nSheet ) < maSheetInfos.size())) ? + &maSheetInfos[ static_cast< size_t >( nSheet ) ] : 0; +} + +OUString WorksheetBuffer::convertToValidSheetName( const OUString& rName, sal_Unicode cReplaceChar ) const +{ + if( !mxCharClass.is() ) return rName; + + using namespace ::com::sun::star::i18n::KParseTokens; + using namespace ::com::sun::star::i18n::KParseType; + + OUStringBuffer aFinalName( rName ); + Locale aLocale( CREATE_OUSTRING( "en" ), CREATE_OUSTRING( "US" ), OUString() ); + sal_Int32 nStartFlags = ANY_LETTER_OR_NUMBER | ASC_UNDERSCORE; + sal_Int32 nContFlags = nStartFlags; + OUString aStartChars; + OUString aContChars( sal_Unicode( ' ' ) ); + sal_Int32 nStartPos = 0; + while( nStartPos < aFinalName.getLength() ) + { + ParseResult aRes = mxCharClass->parsePredefinedToken( + IDENTNAME, rName, nStartPos, aLocale, nStartFlags, aStartChars, nContFlags, aContChars ); + if( aRes.EndPos < aFinalName.getLength() ) + { + aFinalName.setCharAt( aRes.EndPos, cReplaceChar ); + nStartFlags = nContFlags; + aStartChars = aContChars; + } + nStartPos = aRes.EndPos + 1; + } + return aFinalName.makeStringAndClear(); +} + +OUString WorksheetBuffer::insertSheet( const OUString& rName, sal_Int16 nSheet, bool bVisible ) +{ + OUString aFinalName = (rName.getLength() == 0) ? CREATE_OUSTRING( "Sheet" ) : convertToValidSheetName( rName, '_' ); + try + { + Reference< XSpreadsheets > xSheets( getDocument()->getSheets(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSheetsIA( xSheets, UNO_QUERY_THROW ); + Reference< XNameAccess > xSheetsNA( xSheets, UNO_QUERY_THROW ); + PropertySet aPropSet; + if( nSheet < xSheetsIA->getCount() ) + { + // existing sheet - try to rename + Reference< XNamed > xSheetName( xSheetsIA->getByIndex( nSheet ), UNO_QUERY_THROW ); + if( xSheetName->getName() != aFinalName ) + { + aFinalName = ContainerHelper::getUnusedName( xSheetsNA, aFinalName, ' ' ); + xSheetName->setName( aFinalName ); + } + aPropSet.set( xSheetName ); + } + else + { + // new sheet - insert with unused name + aFinalName = ContainerHelper::getUnusedName( xSheetsNA, aFinalName, ' ' ); + xSheets->insertNewByName( aFinalName, nSheet ); + aPropSet.set( xSheetsIA->getByIndex( nSheet ) ); + } + + // sheet properties + aPropSet.setProperty( maIsVisibleProp, bVisible ); + } + catch( Exception& ) + { + OSL_ENSURE( false, "WorksheetBuffer::insertSheet - cannot insert or rename worksheet" ); + } + return aFinalName; +} + +void WorksheetBuffer::insertSheet( const OoxSheetInfo& rSheetInfo ) +{ + OSL_ENSURE( maExternalSheets.empty(), "WorksheetBuffer::insertSheet - external sheets exist already" ); + sal_Int16 nSheet = static_cast< sal_Int16 >( maSheetInfos.size() ); + maSheetInfos.push_back( rSheetInfo ); + maSheetInfos.back().maFinalName = insertSheet( rSheetInfo.maName, nSheet, rSheetInfo.mnState == XML_visible ); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/worksheetfragment.cxx b/oox/source/xls/worksheetfragment.cxx new file mode 100644 index 000000000000..0fce487bb5c7 --- /dev/null +++ b/oox/source/xls/worksheetfragment.cxx @@ -0,0 +1,1068 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: worksheetfragment.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/worksheetfragment.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/core/relations.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/autofiltercontext.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/condformatcontext.hxx" +#include "oox/xls/externallinkbuffer.hxx" +#include "oox/xls/pagesettings.hxx" +#include "oox/xls/pivottablefragment.hxx" +#include "oox/xls/querytablefragment.hxx" +#include "oox/xls/sheetdatacontext.hxx" +#include "oox/xls/tablefragment.hxx" +#include "oox/xls/viewsettings.hxx" +#include "oox/xls/workbooksettings.hxx" +#include "oox/xls/worksheetsettings.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::xml::sax::XFastContextHandler; +using ::oox::core::Relations; +using ::oox::core::RelationsRef; +using ::oox::core::RecordContextRef; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt32 OOBIN_BRK_MANUAL = 0x00000001; + +const sal_uInt16 BIFF_COLINFO_HIDDEN = 0x0001; +const sal_uInt16 BIFF_COLINFO_COLLAPSED = 0x1000; + +const sal_uInt16 BIFF_DEFROW_CUSTOMHEIGHT = 0x0001; +const sal_uInt16 BIFF_DEFROW_HIDDEN = 0x0002; +const sal_uInt16 BIFF_DEFROW_THICKTOP = 0x0004; +const sal_uInt16 BIFF_DEFROW_THICKBOTTOM = 0x0008; +const sal_uInt16 BIFF2_DEFROW_DEFHEIGHT = 0x8000; +const sal_uInt16 BIFF2_DEFROW_MASK = 0x7FFF; + +const sal_uInt32 BIFF_DATAVAL_STRINGLIST = 0x00000080; +const sal_uInt32 BIFF_DATAVAL_ALLOWBLANK = 0x00000100; +const sal_uInt32 BIFF_DATAVAL_NODROPDOWN = 0x00000200; +const sal_uInt32 BIFF_DATAVAL_SHOWINPUT = 0x00040000; +const sal_uInt32 BIFF_DATAVAL_SHOWERROR = 0x00080000; + +const sal_uInt32 BIFF_HYPERLINK_TARGET = 0x00000001; /// File name or URL. +const sal_uInt32 BIFF_HYPERLINK_ABS = 0x00000002; /// Absolute path. +const sal_uInt32 BIFF_HYPERLINK_DISPLAY = 0x00000014; /// Display string. +const sal_uInt32 BIFF_HYPERLINK_LOC = 0x00000008; /// Target location. +const sal_uInt32 BIFF_HYPERLINK_FRAME = 0x00000080; /// Target frame. +const sal_uInt32 BIFF_HYPERLINK_UNC = 0x00000100; /// UNC path. + +} // namespace + +// ============================================================================ + +OoxWorksheetFragment::OoxWorksheetFragment( const WorkbookHelper& rHelper, + const OUString& rFragmentPath, ISegmentProgressBarRef xProgressBar, WorksheetType eSheetType, sal_Int32 nSheet ) : + OoxWorksheetFragmentBase( rHelper, rFragmentPath, xProgressBar, eSheetType, nSheet ) +{ + // import data tables related to this worksheet + RelationsRef xTableRels = getRelations().getRelationsFromType( CREATE_RELATIONS_TYPE( "table" ) ); + for( Relations::const_iterator aIt = xTableRels->begin(), aEnd = xTableRels->end(); aIt != aEnd; ++aIt ) + importOoxFragment( new OoxTableFragment( *this, getFragmentPathFromTarget( aIt->second.maTarget ) ) ); +} + +// oox.xls.OoxContextHelper interface ----------------------------------------- + +bool OoxWorksheetFragment::onCanCreateContext( sal_Int32 nElement ) const +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nElement == XLS_TOKEN( worksheet )); + case XLS_TOKEN( worksheet ): + return (nElement == XLS_TOKEN( sheetPr )) || + (nElement == XLS_TOKEN( dimension )) || + (nElement == XLS_TOKEN( sheetFormatPr )) || + (nElement == XLS_TOKEN( sheetViews )) || + (nElement == XLS_TOKEN( cols )) || + (nElement == XLS_TOKEN( sheetData )) || + (nElement == XLS_TOKEN( mergeCells )) || + (nElement == XLS_TOKEN( hyperlinks )) || + (nElement == XLS_TOKEN( autoFilter )) || + (nElement == XLS_TOKEN( dataValidations )) || + (nElement == XLS_TOKEN( conditionalFormatting )) || + (nElement == XLS_TOKEN( pageMargins )) || + (nElement == XLS_TOKEN( pageSetup )) || + (nElement == XLS_TOKEN( headerFooter )) || + (nElement == XLS_TOKEN( printOptions )) || + (nElement == XLS_TOKEN( rowBreaks )) || + (nElement == XLS_TOKEN( colBreaks )) || + (nElement == XLS_TOKEN( sheetProtection )) || + (nElement == XLS_TOKEN( phoneticPr )); + case XLS_TOKEN( sheetPr ): + return (nElement == XLS_TOKEN( outlinePr )) || + (nElement == XLS_TOKEN( pageSetUpPr )); + case XLS_TOKEN( sheetViews ): + return (nElement == XLS_TOKEN( sheetView )); + case XLS_TOKEN( sheetView ): + return (nElement == XLS_TOKEN( pane )) || + (nElement == XLS_TOKEN( selection )); + case XLS_TOKEN( cols ): + return (nElement == XLS_TOKEN( col )); + case XLS_TOKEN( mergeCells ): + return (nElement == XLS_TOKEN( mergeCell )); + case XLS_TOKEN( hyperlinks ): + return (nElement == XLS_TOKEN( hyperlink )); + case XLS_TOKEN( dataValidations ): + return (nElement == XLS_TOKEN( dataValidation )); + case XLS_TOKEN( dataValidation ): + return (nElement == XLS_TOKEN( formula1 )) || + (nElement == XLS_TOKEN( formula2 )); + case XLS_TOKEN( headerFooter ): + return (nElement == XLS_TOKEN( firstHeader )) || + (nElement == XLS_TOKEN( firstFooter )) || + (nElement == XLS_TOKEN( oddHeader )) || + (nElement == XLS_TOKEN( oddFooter )) || + (nElement == XLS_TOKEN( evenHeader )) || + (nElement == XLS_TOKEN( evenFooter )); + case XLS_TOKEN( rowBreaks ): + case XLS_TOKEN( colBreaks ): + return (nElement == XLS_TOKEN( brk )); + } + return false; +} + +Reference< XFastContextHandler > OoxWorksheetFragment::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) +{ + switch( nElement ) + { + case XLS_TOKEN( sheetData ): + return new OoxSheetDataContext( *this ); + case XLS_TOKEN( autoFilter ): + return new OoxAutoFilterContext( *this ); + case XLS_TOKEN( conditionalFormatting ): + return new OoxCondFormatContext( *this ); + } + return this; +} + +void OoxWorksheetFragment::onStartElement( const AttributeList& rAttribs ) +{ + switch( getCurrentContext() ) + { + case XLS_TOKEN( dimension ): importDimension( rAttribs ); break; + case XLS_TOKEN( sheetFormatPr ): importSheetFormatPr( rAttribs ); break; + case XLS_TOKEN( sheetView ): getSheetViewSettings().importSheetView( rAttribs ); break; + case XLS_TOKEN( pane ): getSheetViewSettings().importPane( rAttribs ); break; + case XLS_TOKEN( selection ): getSheetViewSettings().importSelection( rAttribs ); break; + case XLS_TOKEN( outlinePr ): getWorksheetSettings().importOutlinePr( rAttribs ); break; + case XLS_TOKEN( sheetProtection ): getWorksheetSettings().importSheetProtection( rAttribs ); break; + case XLS_TOKEN( phoneticPr ): getWorksheetSettings().importPhoneticPr( rAttribs ); break; + case XLS_TOKEN( col ): importCol( rAttribs ); break; + case XLS_TOKEN( mergeCell ): importMergeCell( rAttribs ); break; + case XLS_TOKEN( hyperlink ): importHyperlink( rAttribs ); break; + case XLS_TOKEN( dataValidation ): importDataValidation( rAttribs ); break; + case XLS_TOKEN( pageMargins ): getPageSettings().importPageMargins( rAttribs ); break; + case XLS_TOKEN( pageSetup ): getPageSettings().importPageSetup( rAttribs ); break; + case XLS_TOKEN( printOptions ): getPageSettings().importPrintOptions( rAttribs ); break; + case XLS_TOKEN( headerFooter ): getPageSettings().importHeaderFooter( rAttribs ); break; + case XLS_TOKEN( pageSetUpPr ): importPageSetUpPr( rAttribs ); break; + case XLS_TOKEN( brk ): importBrk( rAttribs ); break; + } +} + +void OoxWorksheetFragment::onEndElement( const OUString& rChars ) +{ + sal_Int32 nCurrentContext = getCurrentContext(); + switch( nCurrentContext ) + { + case XLS_TOKEN( firstHeader ): + case XLS_TOKEN( firstFooter ): + case XLS_TOKEN( oddHeader ): + case XLS_TOKEN( oddFooter ): + case XLS_TOKEN( evenHeader ): + case XLS_TOKEN( evenFooter ): + getPageSettings().importHeaderFooterCharacters( rChars, nCurrentContext ); + break; + case XLS_TOKEN( formula1 ): + if( mxValData.get() ) + { + importDataValFormula( mxValData->maTokens1, rChars, mxValData->maRanges.getBaseAddress() ); + // process string list of a list validation (convert to list of string tokens) + if( mxValData->mnType == XML_list ) + getFormulaParser().convertStringToStringList( mxValData->maTokens1, ',', true ); + } + break; + case XLS_TOKEN( formula2 ): + if( mxValData.get() ) + importDataValFormula( mxValData->maTokens2, rChars, mxValData->maRanges.getBaseAddress() ); + break; + case XLS_TOKEN( dataValidation ): + if( mxValData.get() ) + setValidation( *mxValData ); + mxValData.reset(); + break; + } +} + +bool OoxWorksheetFragment::onCanCreateRecordContext( sal_Int32 nRecId ) +{ + switch( getCurrentContext() ) + { + case XML_ROOT_CONTEXT: + return (nRecId == OOBIN_ID_WORKSHEET); + case OOBIN_ID_WORKSHEET: + return (nRecId == OOBIN_ID_SHEETPR) || + (nRecId == OOBIN_ID_DIMENSION) || + (nRecId == OOBIN_ID_SHEETFORMATPR) || + (nRecId == OOBIN_ID_SHEETVIEWS) || + (nRecId == OOBIN_ID_COLS) || + (nRecId == OOBIN_ID_SHEETDATA) || + (nRecId == OOBIN_ID_MERGECELLS) || + (nRecId == OOBIN_ID_HYPERLINK) || + (nRecId == OOBIN_ID_DATAVALIDATIONS) || + (nRecId == OOBIN_ID_CONDFORMATTING) || + (nRecId == OOBIN_ID_PAGEMARGINS) || + (nRecId == OOBIN_ID_PAGESETUP) || + (nRecId == OOBIN_ID_PRINTOPTIONS) || + (nRecId == OOBIN_ID_HEADERFOOTER) || + (nRecId == OOBIN_ID_ROWBREAKS) || + (nRecId == OOBIN_ID_COLBREAKS) || + (nRecId == OOBIN_ID_SHEETPROTECTION) || + (nRecId == OOBIN_ID_PHONETICPR); + case OOBIN_ID_SHEETVIEWS: + return (nRecId == OOBIN_ID_SHEETVIEW); + case OOBIN_ID_SHEETVIEW: + return (nRecId == OOBIN_ID_PANE) || + (nRecId == OOBIN_ID_SELECTION); + case OOBIN_ID_COLS: + return (nRecId == OOBIN_ID_COL); + case OOBIN_ID_MERGECELLS: + return (nRecId == OOBIN_ID_MERGECELL); + case OOBIN_ID_DATAVALIDATIONS: + return (nRecId == OOBIN_ID_DATAVALIDATION); + case OOBIN_ID_ROWBREAKS: + case OOBIN_ID_COLBREAKS: + return (nRecId == OOBIN_ID_BRK); + } + return false; +} + +RecordContextRef OoxWorksheetFragment::onCreateRecordContext( sal_Int32 nRecId, RecordInputStream& ) +{ + switch( nRecId ) + { + case OOBIN_ID_SHEETDATA: + return new OoxSheetDataContext( *this ); + case OOBIN_ID_CONDFORMATTING: + return new OoxCondFormatContext( *this ); + } + return this; +} + +void OoxWorksheetFragment::onStartRecord( RecordInputStream& rStrm ) +{ + switch( getCurrentContext() ) + { + case OOBIN_ID_SHEETPR: getWorksheetSettings().importSheetPr( rStrm ); break; + case OOBIN_ID_DIMENSION: importDimension( rStrm ); break; + case OOBIN_ID_SHEETPROTECTION: getWorksheetSettings().importSheetProtection( rStrm ); break; + case OOBIN_ID_PHONETICPR: getWorksheetSettings().importPhoneticPr( rStrm ); break; + case OOBIN_ID_SHEETFORMATPR: importSheetFormatPr( rStrm ); break; + case OOBIN_ID_SHEETVIEW: getSheetViewSettings().importSheetView( rStrm ); break; + case OOBIN_ID_PANE: getSheetViewSettings().importPane( rStrm ); break; + case OOBIN_ID_SELECTION: getSheetViewSettings().importSelection( rStrm ); break; + case OOBIN_ID_COL: importCol( rStrm ); break; + case OOBIN_ID_MERGECELL: importMergeCell( rStrm ); break; + case OOBIN_ID_HYPERLINK: importHyperlink( rStrm ); break; + case OOBIN_ID_DATAVALIDATION: importDataValidation( rStrm ); break; + case OOBIN_ID_PAGEMARGINS: getPageSettings().importPageMargins( rStrm ); break; + case OOBIN_ID_PAGESETUP: getPageSettings().importPageSetup( rStrm ); break; + case OOBIN_ID_PRINTOPTIONS: getPageSettings().importPrintOptions( rStrm ); break; + case OOBIN_ID_HEADERFOOTER: getPageSettings().importHeaderFooter( rStrm ); break; + case OOBIN_ID_BRK: importBrk( rStrm ); break; + } +} + +// oox.xls.OoxFragmentHandler interface --------------------------------------- + +void OoxWorksheetFragment::initializeImport() +{ + // initial processing in base class WorksheetHelper + initializeWorksheetImport(); + + // import query table fragments related to this worksheet + RelationsRef xQueryRels = getRelations().getRelationsFromType( CREATE_RELATIONS_TYPE( "queryTable" ) ); + for( Relations::const_iterator aIt = xQueryRels->begin(), aEnd = xQueryRels->end(); aIt != aEnd; ++aIt ) + importOoxFragment( new OoxQueryTableFragment( *this, getFragmentPathFromTarget( aIt->second.maTarget ) ) ); + + // import pivot table fragments related to this worksheet + RelationsRef xPivotRels = getRelations().getRelationsFromType( CREATE_RELATIONS_TYPE( "pivotTable" ) ); + for( Relations::const_iterator aIt = xPivotRels->begin(), aEnd = xPivotRels->end(); aIt != aEnd; ++aIt ) + importOoxFragment( new OoxPivotTableFragment( *this, getFragmentPathFromTarget( aIt->second.maTarget ) ) ); +} + +void OoxWorksheetFragment::finalizeImport() +{ + // final processing in base class WorksheetHelper + finalizeWorksheetImport(); +} + +// private -------------------------------------------------------------------- + +void OoxWorksheetFragment::importDimension( const AttributeList& rAttribs ) +{ + CellRangeAddress aRange; + getAddressConverter().convertToCellRangeUnchecked( aRange, rAttribs.getString( XML_ref ), getSheetIndex() ); + setDimension( aRange ); +} + +void OoxWorksheetFragment::importSheetFormatPr( const AttributeList& rAttribs ) +{ + // default column settings + setBaseColumnWidth( rAttribs.getInteger( XML_baseColWidth, 8 ) ); + setDefaultColumnWidth( rAttribs.getDouble( XML_defaultColWidth, 0.0 ) ); + // default row settings + setDefaultRowSettings( + rAttribs.getDouble( XML_defaultRowHeight, 0.0 ), + rAttribs.getBool( XML_customHeight, false ), + rAttribs.getBool( XML_zeroHeight, false ), + rAttribs.getBool( XML_thickTop, false ), + rAttribs.getBool( XML_thickBottom, false ) ); +} + +void OoxWorksheetFragment::importCol( const AttributeList& rAttribs ) +{ + OoxColumnData aData; + aData.mnFirstCol = rAttribs.getInteger( XML_min, -1 ); + aData.mnLastCol = rAttribs.getInteger( XML_max, -1 ); + aData.mfWidth = rAttribs.getDouble( XML_width, 0.0 ); + aData.mnXfId = rAttribs.getInteger( XML_style, -1 ); + aData.mnLevel = rAttribs.getInteger( XML_outlineLevel, 0 ); + aData.mbHidden = rAttribs.getBool( XML_hidden, false ); + aData.mbCollapsed = rAttribs.getBool( XML_collapsed, false ); + // set column properties in the current sheet + setColumnData( aData ); +} + +void OoxWorksheetFragment::importMergeCell( const AttributeList& rAttribs ) +{ + CellRangeAddress aRange; + if( getAddressConverter().convertToCellRange( aRange, rAttribs.getString( XML_ref ), getSheetIndex(), true ) ) + setMergedRange( aRange ); +} + +void OoxWorksheetFragment::importHyperlink( const AttributeList& rAttribs ) +{ + OoxHyperlinkData aData; + if( getAddressConverter().convertToCellRange( aData.maRange, rAttribs.getString( XML_ref ), getSheetIndex(), true ) ) + { + aData.maTarget = getRelations().getTargetFromRelId( rAttribs.getString( R_TOKEN( id ) ) ); + aData.maLocation = rAttribs.getString( XML_location ); + aData.maDisplay = rAttribs.getString( XML_display ); + aData.maTooltip = rAttribs.getString( XML_tooltip ); + setHyperlink( aData ); + } +} + +void OoxWorksheetFragment::importDataValidation( const AttributeList& rAttribs ) +{ + mxValData.reset( new OoxValidationData ); + getAddressConverter().convertToCellRangeList( mxValData->maRanges, rAttribs.getString( XML_sqref ), getSheetIndex(), true ); + mxValData->maInputTitle = rAttribs.getString( XML_promptTitle ); + mxValData->maInputMessage = rAttribs.getString( XML_prompt ); + mxValData->maErrorTitle = rAttribs.getString( XML_errorTitle ); + mxValData->maErrorMessage = rAttribs.getString( XML_error ); + mxValData->mnType = rAttribs.getToken( XML_type, XML_none ); + mxValData->mnOperator = rAttribs.getToken( XML_operator, XML_between ); + mxValData->mnErrorStyle = rAttribs.getToken( XML_errorStyle, XML_stop ); + mxValData->mbShowInputMsg = rAttribs.getBool( XML_showInputMessage, false ); + mxValData->mbShowErrorMsg = rAttribs.getBool( XML_showErrorMessage, false ); + /* The attribute showDropDown@dataValidation is in fact a "suppress + dropdown" flag, as it was in the BIFF format! ECMA specification + and attribute name are plain wrong! */ + mxValData->mbNoDropDown = rAttribs.getBool( XML_showDropDown, false ); + mxValData->mbAllowBlank = rAttribs.getBool( XML_allowBlank, false ); +} + +void OoxWorksheetFragment::importPageSetUpPr( const AttributeList& rAttribs ) +{ + // for whatever reason, this flag is still stored separated from the page settings + getPageSettings().setFitToPagesMode( rAttribs.getBool( XML_fitToPage, false ) ); +} + +void OoxWorksheetFragment::importBrk( const AttributeList& rAttribs ) +{ + OoxPageBreakData aData; + aData.mnColRow = rAttribs.getInteger( XML_id, 0 ); + aData.mnMin = rAttribs.getInteger( XML_id, 0 ); + aData.mnMax = rAttribs.getInteger( XML_id, 0 ); + aData.mbManual = rAttribs.getBool( XML_man, false ); + switch( getPreviousContext() ) + { + case XLS_TOKEN( rowBreaks ): setPageBreak( aData, true ); break; + case XLS_TOKEN( colBreaks ): setPageBreak( aData, false ); break; + } +} + +void OoxWorksheetFragment::importDataValFormula( ApiTokenSequence& orTokens, + const OUString& rFormula, const CellAddress& rBaseAddress ) +{ + TokensFormulaContext aContext( true, false ); + aContext.setBaseAddress( rBaseAddress ); + getFormulaParser().importFormula( aContext, rFormula ); + orTokens = aContext.getTokens(); +} + +void OoxWorksheetFragment::importDimension( RecordInputStream& rStrm ) +{ + BinRange aBinRange; + aBinRange.read( rStrm ); + CellRangeAddress aRange; + getAddressConverter().convertToCellRangeUnchecked( aRange, aBinRange, getSheetIndex() ); + setDimension( aRange ); +} + +void OoxWorksheetFragment::importSheetFormatPr( RecordInputStream& rStrm ) +{ + sal_Int32 nDefaultWidth; + sal_uInt16 nBaseWidth, nDefaultHeight, nFlags; + rStrm >> nDefaultWidth >> nBaseWidth >> nDefaultHeight >> nFlags; + + // base column with + setBaseColumnWidth( nBaseWidth ); + // default width is stored as 1/256th of a character in OOBIN, convert to entire character + setDefaultColumnWidth( static_cast< double >( nDefaultWidth ) / 256.0 ); + // row height is in twips in OOBIN, convert to points; equal flags in BIFF and OOBIN + setDefaultRowSettings( + nDefaultHeight / 20.0, + getFlag( nFlags, BIFF_DEFROW_CUSTOMHEIGHT ), + getFlag( nFlags, BIFF_DEFROW_HIDDEN ), + getFlag( nFlags, BIFF_DEFROW_THICKTOP ), + getFlag( nFlags, BIFF_DEFROW_THICKBOTTOM ) ); +} + +void OoxWorksheetFragment::importCol( RecordInputStream& rStrm ) +{ + OoxColumnData aData; + + sal_uInt16 nWidth, nFlags; + rStrm >> aData.mnFirstCol >> aData.mnLastCol >> nWidth; + rStrm.skip( 2 ); + rStrm >> aData.mnXfId >> nFlags; + + // column indexes are 0-based in OOBIN, but OoxColumnData expects 1-based + ++aData.mnFirstCol; + ++aData.mnLastCol; + // width is stored as 1/256th of a character in OOBIN, convert to entire character + aData.mfWidth = static_cast< double >( nWidth ) / 256.0; + // equal flags in BIFF and OOBIN + aData.mnLevel = extractValue< sal_Int32 >( nFlags, 8, 3 ); + aData.mbHidden = getFlag( nFlags, BIFF_COLINFO_HIDDEN ); + aData.mbCollapsed = getFlag( nFlags, BIFF_COLINFO_COLLAPSED ); + // set column properties in the current sheet + setColumnData( aData ); +} + +void OoxWorksheetFragment::importMergeCell( RecordInputStream& rStrm ) +{ + BinRange aBinRange; + rStrm >> aBinRange; + CellRangeAddress aRange; + if( getAddressConverter().convertToCellRange( aRange, aBinRange, getSheetIndex(), true ) ) + setMergedRange( aRange ); +} + +void OoxWorksheetFragment::importHyperlink( RecordInputStream& rStrm ) +{ + BinRange aBinRange; + rStrm >> aBinRange; + OoxHyperlinkData aData; + if( getAddressConverter().convertToCellRange( aData.maRange, aBinRange, getSheetIndex(), true ) ) + { + aData.maTarget = getRelations().getTargetFromRelId( rStrm.readString() ); + rStrm >> aData.maLocation >> aData.maTooltip >> aData.maDisplay; + setHyperlink( aData ); + } +} + +void OoxWorksheetFragment::importDataValidation( RecordInputStream& rStrm ) +{ + OoxValidationData aData; + + sal_uInt32 nFlags; + BinRangeList aRanges; + rStrm >> nFlags >> aRanges >> aData.maErrorTitle >> aData.maErrorMessage >> aData.maInputTitle >> aData.maInputMessage; + + // equal flags in BIFF and OOBIN + aData.setBinType( extractValue< sal_uInt8 >( nFlags, 0, 4 ) ); + aData.setBinOperator( extractValue< sal_uInt8 >( nFlags, 20, 4 ) ); + aData.setBinErrorStyle( extractValue< sal_uInt8 >( nFlags, 4, 3 ) ); + aData.mbAllowBlank = getFlag( nFlags, BIFF_DATAVAL_ALLOWBLANK ); + aData.mbNoDropDown = getFlag( nFlags, BIFF_DATAVAL_NODROPDOWN ); + aData.mbShowInputMsg = getFlag( nFlags, BIFF_DATAVAL_SHOWINPUT ); + aData.mbShowErrorMsg = getFlag( nFlags, BIFF_DATAVAL_SHOWERROR ); + + // cell range list + getAddressConverter().convertToCellRangeList( aData.maRanges, aRanges, getSheetIndex(), true ); + + // condition formula(s) + TokensFormulaContext aContext( true, false ); + aContext.setBaseAddress( aData.maRanges.getBaseAddress() ); + getFormulaParser().importFormula( aContext, rStrm ); + aData.maTokens1 = aContext.getTokens(); + getFormulaParser().importFormula( aContext, rStrm ); + aData.maTokens2 = aContext.getTokens(); + // process string list of a list validation (convert to list of string tokens) + if( (aData.mnType == XML_list) && getFlag( nFlags, BIFF_DATAVAL_STRINGLIST ) ) + getFormulaParser().convertStringToStringList( aData.maTokens1, ',', true ); + + // set validation data + setValidation( aData ); +} + +void OoxWorksheetFragment::importBrk( RecordInputStream& rStrm ) +{ + OoxPageBreakData aData; + sal_uInt32 nFlags; + rStrm >> aData.mnColRow >> aData.mnMin >> aData.mnMax >> nFlags; + aData.mbManual = getFlag( nFlags, OOBIN_BRK_MANUAL ); + switch( getPreviousContext() ) + { + case OOBIN_ID_ROWBREAKS: setPageBreak( aData, true ); break; + case OOBIN_ID_COLBREAKS: setPageBreak( aData, false ); break; + } +} + +// ============================================================================ + +BiffWorksheetFragment::BiffWorksheetFragment( const WorkbookHelper& rHelper, ISegmentProgressBarRef xProgressBar, WorksheetType eSheetType, sal_Int32 nSheet ) : + BiffWorksheetFragmentBase( rHelper, xProgressBar, eSheetType, nSheet ) +{ +} + +bool BiffWorksheetFragment::importFragment( BiffInputStream& rStrm ) +{ + // initial processing in base class WorksheetHelper + initializeWorksheetImport(); + + // create a SheetDataContext object that implements cell import + BiffSheetDataContext aSheetData( *this ); + + WorkbookSettings& rWorkbookSett = getWorkbookSettings(); + WorksheetSettings& rWorksheetSett = getWorksheetSettings(); + SheetViewSettings& rSheetViewSett = getSheetViewSettings(); + CondFormatBuffer& rCondFormats = getCondFormats(); + PageSettings& rPageSett = getPageSettings(); + + // process all record in this sheet fragment + while( rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) ) + { + sal_uInt16 nRecId = rStrm.getRecId(); + + if( isBofRecord( nRecId ) ) + { + // skip unknown embedded fragments (BOF/EOF blocks) + skipFragment( rStrm ); + } + else + { + // cache core stream position to detect if record is already processed + sal_Int64 nStrmPos = rStrm.getCoreStreamPos(); + + switch( nRecId ) + { + // records in all BIFF versions + case BIFF_ID_BOTTOMMARGIN: rPageSett.importBottomMargin( rStrm ); break; + case BIFF_ID_CALCCOUNT: rWorkbookSett.importCalcCount( rStrm ); break; + case BIFF_ID_CALCMODE: rWorkbookSett.importCalcMode( rStrm ); break; + case BIFF_ID_DEFCOLWIDTH: importDefColWidth( rStrm ); break; + case BIFF_ID_DELTA: rWorkbookSett.importDelta( rStrm ); break; + case BIFF2_ID_DIMENSION: importDimension( rStrm ); break; + case BIFF3_ID_DIMENSION: importDimension( rStrm ); break; + case BIFF_ID_FOOTER: rPageSett.importFooter( rStrm ); break; + case BIFF_ID_HEADER: rPageSett.importHeader( rStrm ); break; + case BIFF_ID_HORPAGEBREAKS: importPageBreaks( rStrm, true ); break; + case BIFF_ID_ITERATION: rWorkbookSett.importIteration( rStrm ); break; + case BIFF_ID_LEFTMARGIN: rPageSett.importLeftMargin( rStrm ); break; + case BIFF_ID_PANE: rSheetViewSett.importPane( rStrm ); break; + case BIFF_ID_PASSWORD: rWorksheetSett.importPassword( rStrm ); break; + case BIFF_ID_PRINTGRIDLINES: rPageSett.importPrintGridLines( rStrm ); break; + case BIFF_ID_PRINTHEADERS: rPageSett.importPrintHeaders( rStrm ); break; + case BIFF_ID_PROTECT: rWorksheetSett.importProtect( rStrm ); break; + case BIFF_ID_REFMODE: rWorkbookSett.importRefMode( rStrm ); break; + case BIFF_ID_RIGHTMARGIN: rPageSett.importRightMargin( rStrm ); break; + case BIFF_ID_SELECTION: rSheetViewSett.importSelection( rStrm ); break; + case BIFF_ID_TOPMARGIN: rPageSett.importTopMargin( rStrm ); break; + case BIFF_ID_VERPAGEBREAKS: importPageBreaks( rStrm, false ); break; + + // BIFF specific records + default: switch( getBiff() ) + { + case BIFF2: switch( nRecId ) + { + case BIFF_ID_COLUMNDEFAULT: importColumnDefault( rStrm ); break; + case BIFF_ID_COLWIDTH: importColWidth( rStrm ); break; + case BIFF2_ID_DEFROWHEIGHT: importDefRowHeight( rStrm ); break; + case BIFF2_ID_WINDOW2: rSheetViewSett.importWindow2( rStrm ); break; + } + break; + + case BIFF3: switch( nRecId ) + { + case BIFF_ID_COLINFO: importColInfo( rStrm ); break; + case BIFF_ID_DEFCOLWIDTH: importDefColWidth( rStrm ); break; + case BIFF3_ID_DEFROWHEIGHT: importDefRowHeight( rStrm ); break; + case BIFF_ID_HCENTER: rPageSett.importHorCenter( rStrm ); break; + case BIFF_ID_OBJECTPROTECT: rWorksheetSett.importObjectProtect( rStrm ); break; + case BIFF_ID_SAVERECALC: rWorkbookSett.importSaveRecalc( rStrm ); break; + case BIFF_ID_SHEETPR: rWorksheetSett.importSheetPr( rStrm ); break; + case BIFF_ID_UNCALCED: rWorkbookSett.importUncalced( rStrm ); break; + case BIFF_ID_VCENTER: rPageSett.importVerCenter( rStrm ); break; + case BIFF3_ID_WINDOW2: rSheetViewSett.importWindow2( rStrm ); break; + + } + break; + + case BIFF4: switch( nRecId ) + { + case BIFF_ID_COLINFO: importColInfo( rStrm ); break; + case BIFF3_ID_DEFROWHEIGHT: importDefRowHeight( rStrm ); break; + case BIFF_ID_HCENTER: rPageSett.importHorCenter( rStrm ); break; + case BIFF_ID_OBJECTPROTECT: rWorksheetSett.importObjectProtect( rStrm ); break; + case BIFF_ID_PAGESETUP: rPageSett.importPageSetup( rStrm ); break; + case BIFF_ID_SAVERECALC: rWorkbookSett.importSaveRecalc( rStrm ); break; + case BIFF_ID_SHEETPR: rWorksheetSett.importSheetPr( rStrm ); break; + case BIFF_ID_STANDARDWIDTH: importStandardWidth( rStrm ); break; + case BIFF_ID_UNCALCED: rWorkbookSett.importUncalced( rStrm ); break; + case BIFF_ID_VCENTER: rPageSett.importVerCenter( rStrm ); break; + case BIFF3_ID_WINDOW2: rSheetViewSett.importWindow2( rStrm ); break; + } + break; + + case BIFF5: switch( nRecId ) + { + case BIFF_ID_COLINFO: importColInfo( rStrm ); break; + case BIFF3_ID_DEFROWHEIGHT: importDefRowHeight( rStrm ); break; + case BIFF_ID_HCENTER: rPageSett.importHorCenter( rStrm ); break; + case BIFF_ID_MERGEDCELLS: importMergedCells( rStrm ); break; // #i62300# also in BIFF5 + case BIFF_ID_OBJECTPROTECT: rWorksheetSett.importObjectProtect( rStrm ); break; + case BIFF_ID_PAGESETUP: rPageSett.importPageSetup( rStrm ); break; + case BIFF_ID_SAVERECALC: rWorkbookSett.importSaveRecalc( rStrm ); break; + case BIFF_ID_SCENPROTECT: rWorksheetSett.importScenProtect( rStrm ); break; + case BIFF_ID_SCL: rSheetViewSett.importScl( rStrm ); break; + case BIFF_ID_SHEETPR: rWorksheetSett.importSheetPr( rStrm ); break; + case BIFF_ID_STANDARDWIDTH: importStandardWidth( rStrm ); break; + case BIFF_ID_UNCALCED: rWorkbookSett.importUncalced( rStrm ); break; + case BIFF_ID_VCENTER: rPageSett.importVerCenter( rStrm ); break; + case BIFF3_ID_WINDOW2: rSheetViewSett.importWindow2( rStrm ); break; + } + break; + + case BIFF8: switch( nRecId ) + { + case BIFF_ID_CFHEADER: rCondFormats.importCfHeader( rStrm ); break; + case BIFF_ID_COLINFO: importColInfo( rStrm ); break; + case BIFF_ID_DATAVALIDATION: importDataValidation( rStrm ); break; + case BIFF_ID_DATAVALIDATIONS: importDataValidations( rStrm ); break; + case BIFF3_ID_DEFROWHEIGHT: importDefRowHeight( rStrm ); break; + case BIFF_ID_HCENTER: rPageSett.importHorCenter( rStrm ); break; + case BIFF_ID_HYPERLINK: importHyperlink( rStrm ); break; + case BIFF_ID_LABELRANGES: importLabelRanges( rStrm ); break; + case BIFF_ID_MERGEDCELLS: importMergedCells( rStrm ); break; + case BIFF_ID_OBJECTPROTECT: rWorksheetSett.importObjectProtect( rStrm ); break; + case BIFF_ID_SAVERECALC: rWorkbookSett.importSaveRecalc( rStrm ); break; + case BIFF_ID_SCENPROTECT: rWorksheetSett.importScenProtect( rStrm ); break; + case BIFF_ID_SCL: rSheetViewSett.importScl( rStrm ); break; + case BIFF_ID_SHEETPR: rWorksheetSett.importSheetPr( rStrm ); break; + case BIFF_ID_SHEETPROTECTION: rWorksheetSett.importSheetProtection( rStrm ); break; + case BIFF_ID_PAGESETUP: rPageSett.importPageSetup( rStrm ); break; + case BIFF_ID_PHONETICPR: rWorksheetSett.importPhoneticPr( rStrm ); break; + case BIFF_ID_STANDARDWIDTH: importStandardWidth( rStrm ); break; + case BIFF_ID_UNCALCED: rWorkbookSett.importUncalced( rStrm ); break; + case BIFF_ID_VCENTER: rPageSett.importVerCenter( rStrm ); break; + case BIFF3_ID_WINDOW2: rSheetViewSett.importWindow2( rStrm ); break; + } + break; + + case BIFF_UNKNOWN: break; + } + } + + // record not processed, try cell records + if( rStrm.getCoreStreamPos() == nStrmPos ) + aSheetData.importRecord( rStrm ); + } + } + + // final processing in base class WorksheetHelper + finalizeWorksheetImport(); + return rStrm.getRecId() == BIFF_ID_EOF; +} + +// private -------------------------------------------------------------------- + +void BiffWorksheetFragment::importColInfo( BiffInputStream& rStrm ) +{ + sal_uInt16 nFirstCol, nLastCol, nWidth, nXfId, nFlags; + rStrm >> nFirstCol >> nLastCol >> nWidth >> nXfId >> nFlags; + + OoxColumnData aData; + // column indexes are 0-based in BIFF, but OoxColumnData expects 1-based + aData.mnFirstCol = static_cast< sal_Int32 >( nFirstCol ) + 1; + aData.mnLastCol = static_cast< sal_Int32 >( nLastCol ) + 1; + // width is stored as 1/256th of a character in BIFF, convert to entire character + aData.mfWidth = static_cast< double >( nWidth ) / 256.0; + aData.mnXfId = nXfId; + aData.mnLevel = extractValue< sal_Int32 >( nFlags, 8, 3 ); + aData.mbHidden = getFlag( nFlags, BIFF_COLINFO_HIDDEN ); + aData.mbCollapsed = getFlag( nFlags, BIFF_COLINFO_COLLAPSED ); + // set column properties in the current sheet + setColumnData( aData ); +} + +void BiffWorksheetFragment::importColumnDefault( BiffInputStream& rStrm ) +{ + sal_uInt16 nFirstCol, nLastCol, nXfId; + rStrm >> nFirstCol >> nLastCol >> nXfId; + convertColumnFormat( nFirstCol, nLastCol, nXfId ); +} + +void BiffWorksheetFragment::importColWidth( BiffInputStream& rStrm ) +{ + sal_uInt8 nFirstCol, nLastCol; + sal_uInt16 nWidth; + rStrm >> nFirstCol >> nLastCol >> nWidth; + + OoxColumnData aData; + // column indexes are 0-based in BIFF, but OoxColumnData expects 1-based + aData.mnFirstCol = static_cast< sal_Int32 >( nFirstCol ) + 1; + aData.mnLastCol = static_cast< sal_Int32 >( nLastCol ) + 1; + // width is stored as 1/256th of a character in BIFF, convert to entire character + aData.mfWidth = static_cast< double >( nWidth ) / 256.0; + // set column properties in the current sheet + setColumnData( aData ); +} + +void BiffWorksheetFragment::importDefColWidth( BiffInputStream& rStrm ) +{ + /* Stored as entire number of characters without padding pixels, which + will be added in setBaseColumnWidth(). Call has no effect, if a + width has already been set from the STANDARDWIDTH record. */ + setBaseColumnWidth( rStrm.readuInt16() ); +} + +void BiffWorksheetFragment::importDefRowHeight( BiffInputStream& rStrm ) +{ + sal_uInt16 nFlags = BIFF_DEFROW_CUSTOMHEIGHT, nHeight; + if( getBiff() != BIFF2 ) + rStrm >> nFlags; + rStrm >> nHeight; + if( getBiff() == BIFF2 ) + nHeight &= BIFF2_DEFROW_MASK; + // row height is in twips in BIFF, convert to points + setDefaultRowSettings( + nHeight / 20.0, + getFlag( nFlags, BIFF_DEFROW_CUSTOMHEIGHT ), + getFlag( nFlags, BIFF_DEFROW_HIDDEN ), + getFlag( nFlags, BIFF_DEFROW_THICKTOP ), + getFlag( nFlags, BIFF_DEFROW_THICKBOTTOM ) ); +} + +void BiffWorksheetFragment::importDataValidations( BiffInputStream& rStrm ) +{ + sal_Int32 nObjId; + rStrm.skip( 10 ); + rStrm >> nObjId; + //! TODO: invalidate object id in drawing object manager +} + +void BiffWorksheetFragment::importDataValidation( BiffInputStream& rStrm ) +{ + OoxValidationData aData; + + // flags + sal_uInt32 nFlags; + rStrm >> nFlags; + aData.setBinType( extractValue< sal_uInt8 >( nFlags, 0, 4 ) ); + aData.setBinOperator( extractValue< sal_uInt8 >( nFlags, 20, 4 ) ); + aData.setBinErrorStyle( extractValue< sal_uInt8 >( nFlags, 4, 3 ) ); + aData.mbAllowBlank = getFlag( nFlags, BIFF_DATAVAL_ALLOWBLANK ); + aData.mbNoDropDown = getFlag( nFlags, BIFF_DATAVAL_NODROPDOWN ); + aData.mbShowInputMsg = getFlag( nFlags, BIFF_DATAVAL_SHOWINPUT ); + aData.mbShowErrorMsg = getFlag( nFlags, BIFF_DATAVAL_SHOWERROR ); + + /* Message strings. Empty strings are single NUL characters (string length + is 1). Do not let the stream replace them with '?' characters. */ + rStrm.setNulSubstChar( '\0' ); + aData.maInputTitle = rStrm.readUniString(); + aData.maErrorTitle = rStrm.readUniString(); + aData.maInputMessage = rStrm.readUniString(); + aData.maErrorMessage = rStrm.readUniString(); + + /* Condition formula(s). String list is single tStr token with NUL + separators, replace them with LF characters. */ + rStrm.setNulSubstChar( '\n' ); + readDataValFormula( aData.maTokens1, rStrm ); + readDataValFormula( aData.maTokens2, rStrm ); + rStrm.setNulSubstChar(); // back to default + // process string list of a list validation (convert to list of string tokens) + if( (aData.mnType == XML_list) && getFlag( nFlags, BIFF_DATAVAL_STRINGLIST ) ) + getFormulaParser().convertStringToStringList( aData.maTokens1, '\n', true ); + + // cell range list + BinRangeList aRanges; + rStrm >> aRanges; + getAddressConverter().convertToCellRangeList( aData.maRanges, aRanges, getSheetIndex(), true ); + + // set validation data + setValidation( aData ); +} + +void BiffWorksheetFragment::importDimension( BiffInputStream& rStrm ) +{ + BinRange aBinRange; + aBinRange.read( rStrm, true, (rStrm.getRecId() == BIFF3_ID_DIMENSION) && (getBiff() == BIFF8) ); + // first unused row/column index in BIFF, not last used + if( aBinRange.maFirst.mnCol < aBinRange.maLast.mnCol ) --aBinRange.maLast.mnCol; + if( aBinRange.maFirst.mnRow < aBinRange.maLast.mnRow ) --aBinRange.maLast.mnRow; + // set dimension + CellRangeAddress aRange; + getAddressConverter().convertToCellRangeUnchecked( aRange, aBinRange, getSheetIndex() ); + setDimension( aRange ); +} + +void BiffWorksheetFragment::importHyperlink( BiffInputStream& rStrm ) +{ + OoxHyperlinkData aData; + + // read cell range for the hyperlink + BinRange aBiffRange; + rStrm >> aBiffRange; + // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?) + aBiffRange.maFirst.mnCol &= 0xFF; + aBiffRange.maLast.mnCol &= 0xFF; + if( !getAddressConverter().convertToCellRange( aData.maRange, aBiffRange, getSheetIndex(), true ) ) + return; + + BiffGuid aGuid; + sal_uInt32 nId, nFlags; + rStrm >> aGuid >> nId >> nFlags; + + OSL_ENSURE( aGuid == BiffHelper::maGuidStdHlink, "BiffWorksheetFragment::importHyperlink - unexpected header GUID" ); + OSL_ENSURE( nId == 2, "BiffWorksheetFragment::importHyperlink - unexpected header identifier" ); + if( !(aGuid == BiffHelper::maGuidStdHlink) ) + return; + + // display string + if( getFlag( nFlags, BIFF_HYPERLINK_DISPLAY ) ) + aData.maDisplay = readHyperlinkString( rStrm, true ); + // target frame (ignore) !TODO: DISPLAY/FRAME - right order? (never seen them together) + if( getFlag( nFlags, BIFF_HYPERLINK_FRAME ) ) + skipHyperlinkString( rStrm, true ); + + // target + if( getFlag( nFlags, BIFF_HYPERLINK_TARGET ) ) + { + if( getFlag( nFlags, BIFF_HYPERLINK_UNC ) ) + { + // UNC path + OSL_ENSURE( getFlag( nFlags, BIFF_HYPERLINK_ABS ), "BiffWorksheetFragment::importHyperlink - UNC link not absolute" ); + aData.maTarget = readHyperlinkString( rStrm, true ); + } + else + { + rStrm >> aGuid; + if( aGuid == BiffHelper::maGuidFileMoniker ) + { + // file name, maybe relative and with directory up-count + sal_Int16 nUpLevels; + rStrm >> nUpLevels; + OSL_ENSURE( (nUpLevels == 0) || !getFlag( nFlags, BIFF_HYPERLINK_ABS ), "BiffWorksheetFragment::importHyperlink - absolute filename with upcount" ); + OUString aShortName = readHyperlinkString( rStrm, false ); + if( rStrm.skip( 24 ).readInt32() > 0 ) + { + sal_Int32 nStrLen = rStrm.readInt32() / 2; // byte count to char count + rStrm.skip( 2 ); + aData.maTarget = readHyperlinkString( rStrm, nStrLen, true ); + } + if( aData.maTarget.getLength() == 0 ) + aData.maTarget = aShortName; + if( !getFlag( nFlags, BIFF_HYPERLINK_ABS ) ) + for( sal_uInt16 nLevel = 0; nLevel < nUpLevels; ++nLevel ) + aData.maTarget = CREATE_OUSTRING( "..\\" ) + aData.maTarget; + } + else if( aGuid == BiffHelper::maGuidUrlMoniker ) + { + // URL, maybe relative and with leading '../' + sal_Int32 nStrLen = rStrm.readInt32() / 2; // byte count to char count + aData.maTarget = readHyperlinkString( rStrm, nStrLen, true ); + } + else + { + OSL_ENSURE( false, "BiffWorksheetFragment::importHyperlink - unknown content GUID" ); + return; + } + } + } + + // target location + if( getFlag( nFlags, BIFF_HYPERLINK_LOC ) ) + aData.maLocation = readHyperlinkString( rStrm, true ); + + OSL_ENSURE( rStrm.getRecLeft() == 0, "BiffWorksheetFragment::importHyperlink - unknown record data" ); + + // try to read the SCREENTIP record + if( (rStrm.getNextRecId() == BIFF_ID_SCREENTIP) && rStrm.startNextRecord() ) + { + rStrm.skip( 2 ); // repeated record id + // the cell range, again + rStrm >> aBiffRange; + CellRangeAddress aRange; + if( getAddressConverter().convertToCellRange( aRange, aBiffRange, getSheetIndex(), true ) && + (aRange.StartColumn == aData.maRange.StartColumn) && + (aRange.StartRow == aData.maRange.StartRow) && + (aRange.EndColumn == aData.maRange.EndColumn) && + (aRange.EndRow == aData.maRange.EndRow) ) + { + /* This time, we have no string length, no flag field, and a + null-terminated 16-bit character array. */ + aData.maTooltip = rStrm.readUnicodeArray( static_cast< sal_uInt16 >( rStrm.getRecLeft() / 2 ) ); + } + } + + // store the hyperlink settings + setHyperlink( aData ); +} + +void BiffWorksheetFragment::importLabelRanges( BiffInputStream& rStrm ) +{ + BinRangeList aBiffRowRanges, aBiffColRanges; + rStrm >> aBiffRowRanges >> aBiffColRanges; + ApiCellRangeList aColRanges, aRowRanges; + getAddressConverter().convertToCellRangeList( aColRanges, aBiffColRanges, getSheetIndex(), true ); + getAddressConverter().convertToCellRangeList( aRowRanges, aBiffRowRanges, getSheetIndex(), true ); + setLabelRanges( aColRanges, aRowRanges ); +} + +void BiffWorksheetFragment::importMergedCells( BiffInputStream& rStrm ) +{ + BinRangeList aBiffRanges; + rStrm >> aBiffRanges; + ApiCellRangeList aRanges; + getAddressConverter().convertToCellRangeList( aRanges, aBiffRanges, getSheetIndex(), true ); + for( ApiCellRangeList::const_iterator aIt = aRanges.begin(), aEnd = aRanges.end(); aIt != aEnd; ++aIt ) + setMergedRange( *aIt ); +} + +void BiffWorksheetFragment::importPageBreaks( BiffInputStream& rStrm, bool bRowBreak ) +{ + OoxPageBreakData aData; + aData.mbManual = true; // only manual breaks stored in BIFF + bool bBiff8 = getBiff() == BIFF8; // skip start/end columns or rows in BIFF8 + + sal_uInt16 nCount; + rStrm >> nCount; + for( sal_uInt16 nIndex = 0; rStrm.isValid() && (nIndex < nCount); ++nIndex ) + { + aData.mnColRow = rStrm.readuInt16(); + setPageBreak( aData, bRowBreak ); + if( bBiff8 ) + rStrm.skip( 4 ); + } +} + +void BiffWorksheetFragment::importStandardWidth( BiffInputStream& rStrm ) +{ + sal_uInt16 nWidth; + rStrm >> nWidth; + // width is stored as 1/256th of a character in BIFF, convert to entire character + double fWidth = static_cast< double >( nWidth ) / 256.0; + // set as default width, will override the width from DEFCOLWIDTH record + setDefaultColumnWidth( fWidth ); +} + +void BiffWorksheetFragment::readDataValFormula( ApiTokenSequence& orTokens, BiffInputStream& rStrm ) +{ + sal_uInt16 nFmlaSize = rStrm.readuInt16(); + rStrm.skip( 2 ); + TokensFormulaContext aContext( true, false ); + getFormulaParser().importFormula( aContext, rStrm, &nFmlaSize ); + orTokens = aContext.getTokens(); +} + +OUString BiffWorksheetFragment::readHyperlinkString( BiffInputStream& rStrm, sal_Int32 nChars, bool bUnicode ) +{ + OUString aRet; + if( nChars > 0 ) + { + sal_uInt16 nReadChars = getLimitedValue< sal_uInt16, sal_Int32 >( nChars, 0, SAL_MAX_UINT16 ); + aRet = bUnicode ? + rStrm.readUnicodeArray( nReadChars ) : + rStrm.readCharArray( nReadChars, getTextEncoding() ); + // skip remaining chars + sal_uInt32 nSkip = static_cast< sal_uInt32 >( nChars - nReadChars ); + rStrm.skip( bUnicode ? (nSkip * 2) : nSkip ); + } + return aRet; +} + +OUString BiffWorksheetFragment::readHyperlinkString( BiffInputStream& rStrm, bool bUnicode ) +{ + return readHyperlinkString( rStrm, rStrm.readInt32(), bUnicode ); +} + +void BiffWorksheetFragment::skipHyperlinkString( BiffInputStream& rStrm, sal_Int32 nChars, bool bUnicode ) +{ + if( nChars > 0 ) + rStrm.skip( static_cast< sal_uInt32 >( bUnicode ? (nChars * 2) : nChars ) ); +} + +void BiffWorksheetFragment::skipHyperlinkString( BiffInputStream& rStrm, bool bUnicode ) +{ + skipHyperlinkString( rStrm, rStrm.readInt32(), bUnicode ); +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/worksheethelper.cxx b/oox/source/xls/worksheethelper.cxx new file mode 100644 index 000000000000..e6fdbd88d85a --- /dev/null +++ b/oox/source/xls/worksheethelper.cxx @@ -0,0 +1,1652 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: worksheethelper.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:09 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/worksheethelper.hxx" +#include <utility> +#include <list> +#include <rtl/ustrbuf.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/util/XMergeable.hpp> +#include <com/sun/star/table/XColumnRowRange.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp> +#include <com/sun/star/sheet/XCellAddressable.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/sheet/XFormulaTokens.hpp> +#include <com/sun/star/sheet/XSheetOutline.hpp> +#include <com/sun/star/sheet/XMultipleOperation.hpp> +#include <com/sun/star/sheet/XLabelRanges.hpp> +#include <com/sun/star/text/XText.hpp> +#include "oox/helper/containerhelper.hxx" +#include "oox/helper/propertyset.hxx" +#include "oox/core/filterbase.hxx" +#include "oox/xls/addressconverter.hxx" +#include "oox/xls/condformatbuffer.hxx" +#include "oox/xls/formulaparser.hxx" +#include "oox/xls/ooxtokens.hxx" +#include "oox/xls/pagesettings.hxx" +#include "oox/xls/sharedformulabuffer.hxx" +#include "oox/xls/sharedstringsbuffer.hxx" +#include "oox/xls/stylesbuffer.hxx" +#include "oox/xls/unitconverter.hxx" +#include "oox/xls/validationpropertyhelper.hxx" +#include "oox/xls/viewsettings.hxx" +#include "oox/xls/worksheetbuffer.hxx" +#include "oox/xls/worksheetsettings.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::util::XMergeable; +using ::com::sun::star::table::CellAddress; +using ::com::sun::star::table::CellRangeAddress; +using ::com::sun::star::table::BorderLine; +using ::com::sun::star::table::XColumnRowRange; +using ::com::sun::star::table::XTableColumns; +using ::com::sun::star::table::XTableRows; +using ::com::sun::star::table::XCell; +using ::com::sun::star::table::XCellRange; +using ::com::sun::star::sheet::XSpreadsheet; +using ::com::sun::star::sheet::XSheetCellRanges; +using ::com::sun::star::sheet::XSheetCellRangeContainer; +using ::com::sun::star::sheet::XCellAddressable; +using ::com::sun::star::sheet::XCellRangeAddressable; +using ::com::sun::star::sheet::XFormulaTokens; +using ::com::sun::star::sheet::XSheetOutline; +using ::com::sun::star::sheet::XMultipleOperation; +using ::com::sun::star::sheet::XLabelRanges; +using ::com::sun::star::text::XText; +using ::com::sun::star::text::XTextContent; +using ::com::sun::star::text::XTextRange; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +void lclUpdateProgressBar( ISegmentProgressBarRef xProgressBar, const CellRangeAddress& rDimension, sal_Int32 nRow ) +{ + if( xProgressBar.get() && (rDimension.StartRow <= nRow) && (nRow <= rDimension.EndRow) ) + { + double fPosition = static_cast< double >( nRow - rDimension.StartRow + 1 ) / (rDimension.EndRow - rDimension.StartRow + 1); + if( xProgressBar->getPosition() < fPosition ) + xProgressBar->setPosition( fPosition ); + } +} + +} // namespace + +// ============================================================================ +// ============================================================================ + +void OoxCellData::reset() +{ + mxCell.clear(); + maValueStr = maFormulaRef = OUString(); + mnCellType = mnFormulaType = XML_TOKEN_INVALID; + mnSharedId = mnXfId = mnNumFmtId = -1; + mbHasValueStr = mbShowPhonetic = false; +} + +// ---------------------------------------------------------------------------- + +OoxDataTableData::OoxDataTableData() : + mb2dTable( false ), + mbRowTable( false ), + mbRef1Deleted( false ), + mbRef2Deleted( false ) +{ +} + +// ---------------------------------------------------------------------------- + +OoxColumnData::OoxColumnData() : + mnFirstCol( -1 ), + mnLastCol( -1 ), + mfWidth( 0.0 ), + mnXfId( -1 ), + mnLevel( 0 ), + mbHidden( false ), + mbCollapsed( false ) +{ +} + +bool OoxColumnData::tryExpand( const OoxColumnData& rNewData ) +{ + bool bExpandable = + (mnFirstCol <= rNewData.mnFirstCol) && + (rNewData.mnFirstCol <= mnLastCol + 1) && + (mfWidth == rNewData.mfWidth) && + // ignore mnXfId, cell formatting is always set directly + (mnLevel == rNewData.mnLevel) && + (mbHidden == rNewData.mbHidden) && + (mbCollapsed == rNewData.mbCollapsed); + if( bExpandable ) + mnLastCol = rNewData.mnLastCol; + return bExpandable; +} + +// ---------------------------------------------------------------------------- + +OoxRowData::OoxRowData() : + mnFirstRow( -1 ), + mnLastRow( -1 ), + mfHeight( 0.0 ), + mnXfId( -1 ), + mnLevel( 0 ), + mbCustomHeight( false ), + mbCustomFormat( false ), + mbShowPhonetic( false ), + mbHidden( false ), + mbCollapsed( false ), + mbThickTop( false ), + mbThickBottom( false ) +{ +} + +bool OoxRowData::tryExpand( const OoxRowData& rNewData ) +{ + bool bExpandable = + (mnFirstRow <= rNewData.mnFirstRow) && + (rNewData.mnFirstRow <= mnLastRow + 1) && + (mfHeight == rNewData.mfHeight) && + // ignore mnXfId, mbCustomFormat, mbShowPhonetic - cell formatting is always set directly + (mnLevel == rNewData.mnLevel) && + (mbCustomHeight == rNewData.mbCustomHeight) && + (mbHidden == rNewData.mbHidden) && + (mbCollapsed == rNewData.mbCollapsed); + if( bExpandable ) + mnLastRow = rNewData.mnLastRow; + return bExpandable; +} + +// ---------------------------------------------------------------------------- + +OoxPageBreakData::OoxPageBreakData() : + mnColRow( 0 ), + mbManual( false ) +{ +} + +// ---------------------------------------------------------------------------- + +OoxHyperlinkData::OoxHyperlinkData() +{ +} + +// ---------------------------------------------------------------------------- + +OoxValidationData::OoxValidationData() : + mnType( XML_none ), + mnOperator( XML_between ), + mnErrorStyle( XML_stop ), + mbShowInputMsg( false ), + mbShowErrorMsg( false ), + mbNoDropDown( false ), + mbAllowBlank( false ) +{ +} + +void OoxValidationData::setBinType( sal_uInt8 nType ) +{ + static const sal_Int32 spnTypeIds[] = { + XML_none, XML_whole, XML_decimal, XML_list, XML_date, XML_time, XML_textLength, XML_custom }; + mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_none ); +} + +void OoxValidationData::setBinOperator( sal_uInt8 nOperator ) +{ + static const sal_Int32 spnOperators[] = { + XML_between, XML_notBetween, XML_equal, XML_notEqual, + XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual }; + mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID ); +} + +void OoxValidationData::setBinErrorStyle( sal_uInt8 nErrorStyle ) +{ + static const sal_Int32 spnErrorStyles[] = { XML_stop, XML_warning, XML_information }; + mnErrorStyle = STATIC_ARRAY_SELECT( spnErrorStyles, nErrorStyle, XML_stop ); +} + +// ============================================================================ +// ============================================================================ + +class WorksheetData : public WorkbookHelper +{ +public: + explicit WorksheetData( + const WorkbookHelper& rHelper, + ISegmentProgressBarRef xProgressBar, + WorksheetType eSheetType, + sal_Int32 nSheet ); + + /** Returns true, if this helper refers to an existing Calc sheet. */ + inline bool isValidSheet() const { return mxSheet.is(); } + + /** Returns a cell formula simulating an empty string result. */ + inline const OUString& getEmptyStringFormula() const { return maEmptyStrFormula; } + /** Returns a cell formula simulating the passed boolean value. */ + const OUString& getBooleanFormula( bool bValue ) const; + + /** Returns the type of this sheet. */ + inline WorksheetType getSheetType() const { return meSheetType; } + /** Returns the index of the current sheet. */ + inline sal_Int16 getSheetIndex() const { return mnSheet; } + /** Returns the XSpreadsheet interface of the current sheet. */ + inline const ::com::sun::star::uno::Reference< ::com::sun::star::sheet::XSpreadsheet >& + getXSpreadsheet() const { return mxSheet; } + + /** Returns the XCell interface for the passed cell address. */ + Reference< XCell > getCell( const CellAddress& rAddress ) const; + /** Returns the XCellRange interface for the passed cell range address. */ + Reference< XCellRange > getCellRange( const CellRangeAddress& rRange ) const; + /** Returns the XSheetCellRanges interface for the passed cell range addresses. */ + Reference< XSheetCellRanges > getCellRangeList( const ApiCellRangeList& rRanges ) const; + + /** Returns the XCellRange interface for a column. */ + Reference< XCellRange > getColumn( sal_Int32 nCol ) const; + /** Returns the XCellRange interface for a row. */ + Reference< XCellRange > getRow( sal_Int32 nRow ) const; + + /** Returns the XTableColumns interface for a range of columns. */ + Reference< XTableColumns > getColumns( sal_Int32 nFirstCol, sal_Int32 nLastCol ) const; + /** Returns the XTableRows interface for a range of rows. */ + Reference< XTableRows > getRows( sal_Int32 nFirstRow, sal_Int32 nLastRow ) const; + + /** Returns the worksheet settings object. */ + inline WorksheetSettings& getWorksheetSettings() { return maSheetSett; } + /** Returns the buffer containing all shared formulas in this sheet. */ + inline SharedFormulaBuffer& getSharedFormulas() { return maSharedFmlas; } + /** Returns the conditional formattings in this sheet. */ + inline CondFormatBuffer& getCondFormats() { return maCondFormats; } + /** Returns the page/print settings for this sheet. */ + inline PageSettings& getPageSettings() { return maPageSett; } + /** Returns the view settings for this sheet. */ + inline SheetViewSettings& getSheetViewSettings() { return maSheetViewSett; } + + /** Sets the dimension (used area) of the sheet. */ + void setDimension( const CellRangeAddress& rRange ); + /** Stores the cell format at the passed address. */ + void setCellFormat( const OoxCellData& rCellData ); + /** Merges the cells in the passed cell range. */ + void setMergedRange( const CellRangeAddress& rRange ); + /** Sets a column or row page break described in the passed struct. */ + void setPageBreak( const OoxPageBreakData& rData, bool bRowBreak ); + /** Inserts the hyperlink URL into the spreadsheet. */ + void setHyperlink( const OoxHyperlinkData& rHyperlink ); + /** Inserts the data validation settings into the spreadsheet. */ + void setValidation( const OoxValidationData& rValData ); + + /** Sets base width for all columns (without padding pixels). This value + is only used, if base width has not been set with setDefaultColumnWidth(). */ + void setBaseColumnWidth( sal_Int32 nWidth ); + /** Sets default width for all columns. This function overrides the base + width set with the setBaseColumnWidth() function. */ + void setDefaultColumnWidth( double fWidth ); + /** Sets column settings for a specific column range. + @descr Column default formatting is converted directly, other settings + are cached and converted in the finalizeImport() call. */ + void setColumnData( const OoxColumnData& rData ); + + /** Sets default height and hidden state for all unused rows in the sheet. */ + void setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom ); + /** Sets row settings for a specific row. + @descr Row default formatting is converted directly, other settings + are cached and converted in the finalizeImport() call. */ + void setRowData( const OoxRowData& rData ); + + /** Converts column default cell formatting. */ + void convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId ); + /** Converts row default cell formatting. */ + void convertRowFormat( sal_Int32 nFirstRow, sal_Int32 nLastRow, sal_Int32 nXfId ); + + /** Initial conversion before importing the worksheet. */ + void initializeWorksheetImport(); + /** Final conversion after importing the worksheet. */ + void finalizeWorksheetImport(); + +private: + typedef ::std::vector< sal_Int32 > OutlineLevelVec; + typedef ::std::map< sal_Int32, OoxColumnData > OoxColumnDataMap; + typedef ::std::map< sal_Int32, OoxRowData > OoxRowDataMap; + typedef ::std::list< OoxHyperlinkData > OoxHyperlinkList; + typedef ::std::list< OoxValidationData > OoxValidationList; + + struct XfIdRange + { + CellRangeAddress maRange; /// The formatted cell range. + sal_Int32 mnXfId; /// XF identifier for the range. + sal_Int32 mnNumFmtId; /// Number format id overriding the XF. + + void set( const OoxCellData& rCellData ); + bool tryExpand( const OoxCellData& rCellData ); + bool tryMerge( const XfIdRange& rXfIdRange ); + }; + + struct MergedRange + { + CellRangeAddress maRange; /// The formatted cell range. + sal_Int32 mnHorAlign; /// Horizontal alignment in the range. + + explicit MergedRange( const CellRangeAddress& rAddress ); + explicit MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ); + bool tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign ); + }; + + typedef ::std::pair< sal_Int32, sal_Int32 > RowColKey; + typedef ::std::map< RowColKey, XfIdRange > XfIdRangeMap; + typedef ::std::list< MergedRange > MergedRangeList; + + /** Writes all cell formatting attributes to the passed cell range. */ + void writeXfIdRangeProperties( const XfIdRange& rXfIdRange ) const; + /** Tries to merge the ranges last inserted in maXfIdRanges with existing ranges. */ + void mergeXfIdRanges(); + /** Finalizes the remaining ranges in maXfIdRanges. */ + void finalizeXfIdRanges(); + + /** Inserts all imported hyperlinks into their cell ranges. */ + void finalizeHyperlinkRanges() const; + /** Inserts a hyperlinks into the specified cell. */ + void finalizeHyperlink( const CellAddress& rAddress, const OUString& rUrl ) const; + + /** Inserts all imported data validations into their cell ranges. */ + void finalizeValidationRanges() const; + + /** Merges all cached merged ranges and updates right/bottom cell borders. */ + void finalizeMergedRanges() const; + /** Merges the passed merged range and updates right/bottom cell borders. */ + void finalizeMergedRange( const CellRangeAddress& rRange ) const; + + /** Converts column properties for all columns in the sheet. */ + void convertColumns(); + /** Converts column properties. */ + void convertColumns( OutlineLevelVec& orColLevels, sal_Int32 nFirstCol, sal_Int32 nLastCol, const OoxColumnData& rData ); + + /** Converts row properties for all rows in the sheet. */ + void convertRows(); + /** Converts row properties. */ + void convertRows( OutlineLevelVec& orRowLevels, sal_Int32 nFirstRow, sal_Int32 nLastRow, const OoxRowData& rData, double fDefHeight = -1.0 ); + + /** Converts outline grouping for the passed column or row. */ + void convertOutlines( OutlineLevelVec& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows ); + /** Groups columns or rows for the given range. */ + void groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapsed, bool bRows ); + +private: + const OUString maEmptyStrFormula; /// Replacement formula for empty string result. + const OUString maTrueFormula; /// Replacement formula for TRUE boolean cells. + const OUString maFalseFormula; /// Replacement formula for FALSE boolean cells. + const OUString maSheetCellRanges; /// Service name for a SheetCellRanges object. + const OUString maRightBorderProp; /// Property name of the right border of a cell. + const OUString maBottomBorderProp; /// Property name of the bottom border of a cell. + const OUString maWidthProp; /// Property name for column width. + const OUString maHeightProp; /// Property name for row height. + const OUString maVisibleProp; /// Property name for column/row visibility. + const OUString maPageBreakProp; /// Property name of a page break. + const OUString maUrlTextField; /// Service name for a URL text field. + const OUString maUrlProp; /// Property name for the URL string in a URL text field. + const OUString maReprProp; /// Property name for the URL representation in a URL text field. + const CellAddress& mrMaxApiPos; /// Reference to maximum Calc cell address from address converter. + CellRangeAddress maDimension; /// Dimension (used) area of the sheet. + OoxColumnData maDefColData; /// Default column formatting. + OoxColumnDataMap maColDatas; /// Column data sorted by first column index. + OoxRowData maDefRowData; /// Default row formatting. + OoxRowDataMap maRowDatas; /// Row data sorted by row index. + OoxHyperlinkList maHyperlinks; /// Cell ranges containing hyperlinks. + OoxValidationList maValidations; /// Cell ranges containing data validation settings. + XfIdRangeMap maXfIdRanges; /// Collected XF identifiers for cell ranges. + MergedRangeList maMergedRanges; /// Merged cell ranges. + MergedRangeList maCenterFillRanges; /// Merged cell ranges from 'center across' or 'fill' alignment. + WorksheetSettings maSheetSett; /// Global settings for this sheet. + SharedFormulaBuffer maSharedFmlas; /// Buffer for shared formulas in this sheet. + CondFormatBuffer maCondFormats; /// Buffer for conditional formattings. + PageSettings maPageSett; /// Page/print settings for this sheet. + SheetViewSettings maSheetViewSett; /// View settings for this sheet. + ISegmentProgressBarRef mxProgressBar; /// Sheet progress bar. + ISegmentProgressBarRef mxRowProgress; /// Progress bar for row/cell processing. + ISegmentProgressBarRef mxFinalProgress; /// Progress bar for finalization. + const WorksheetType meSheetType; /// Type of thes sheet. + Reference< XSpreadsheet > mxSheet; /// Reference to the current sheet. + sal_Int16 mnSheet; /// Index of the current sheet. + bool mbHasDefWidth; /// True = default column width is set from defaultColWidth attribute. +}; + +// ---------------------------------------------------------------------------- + +WorksheetData::WorksheetData( const WorkbookHelper& rHelper, ISegmentProgressBarRef xProgressBar, WorksheetType eSheetType, sal_Int32 nSheet ) : + WorkbookHelper( rHelper ), + maEmptyStrFormula( CREATE_OUSTRING( "=\"\"" ) ), + maTrueFormula( CREATE_OUSTRING( "=TRUE()" ) ), + maFalseFormula( CREATE_OUSTRING( "=FALSE()" ) ), + maSheetCellRanges( CREATE_OUSTRING( "com.sun.star.sheet.SheetCellRanges" ) ), + maRightBorderProp( CREATE_OUSTRING( "RightBorder" ) ), + maBottomBorderProp( CREATE_OUSTRING( "BottomBorder" ) ), + maWidthProp( CREATE_OUSTRING( "Width" ) ), + maHeightProp( CREATE_OUSTRING( "Height" ) ), + maVisibleProp( CREATE_OUSTRING( "IsVisible" ) ), + maPageBreakProp( CREATE_OUSTRING( "IsStartOfNewPage" ) ), + maUrlTextField( CREATE_OUSTRING( "com.sun.star.text.TextField.URL" ) ), + maUrlProp( CREATE_OUSTRING( "URL" ) ), + maReprProp( CREATE_OUSTRING( "Representation" ) ), + mrMaxApiPos( rHelper.getAddressConverter().getMaxApiAddress() ), + maSheetSett( WorksheetHelper( *this ) ), + maSharedFmlas( WorksheetHelper( *this ) ), + maCondFormats( WorksheetHelper( *this ) ), + maPageSett( WorksheetHelper( *this ) ), + maSheetViewSett( WorksheetHelper( *this ) ), + mxProgressBar( xProgressBar ), + meSheetType( eSheetType ), + mnSheet( static_cast< sal_Int16 >( nSheet ) ), + mbHasDefWidth( false ) +{ + OSL_ENSURE( nSheet <= SAL_MAX_INT16, "WorksheetData::WorksheetData - invalid sheet index" ); + mxSheet = getSheet( nSheet ); + if( !mxSheet.is() ) + mnSheet = -1; + + maDimension.Sheet = mnSheet; + + // default column settings (width and hidden state may be updated later) + maDefColData.mfWidth = 8.5; + maDefColData.mnXfId = -1; + maDefColData.mnLevel = 0; + maDefColData.mbHidden = false; + maDefColData.mbCollapsed = false; + + // default row settings (height and hidden state may be updated later) + maDefRowData.mfHeight = 0.0; + maDefRowData.mnXfId = -1; + maDefRowData.mnLevel = 0; + maDefRowData.mbCustomHeight = false; + maDefRowData.mbCustomFormat = false; + maDefRowData.mbShowPhonetic = false; + maDefRowData.mbHidden = false; + maDefRowData.mbCollapsed = false; + + if( mxProgressBar.get() ) + { + mxRowProgress = mxProgressBar->createSegment( 0.5 ); + mxFinalProgress = mxProgressBar->createSegment( 0.5 ); + } +} + +const OUString& WorksheetData::getBooleanFormula( bool bValue ) const +{ + return bValue ? maTrueFormula : maFalseFormula; +} + +Reference< XCell > WorksheetData::getCell( const CellAddress& rAddress ) const +{ + Reference< XCell > xCell; + if( mxSheet.is() ) try + { + xCell = mxSheet->getCellByPosition( rAddress.Column, rAddress.Row ); + } + catch( Exception& ) + { + } + return xCell; +} + +Reference< XCellRange > WorksheetData::getCellRange( const CellRangeAddress& rRange ) const +{ + Reference< XCellRange > xRange; + if( mxSheet.is() ) try + { + xRange = mxSheet->getCellRangeByPosition( rRange.StartColumn, rRange.StartRow, rRange.EndColumn, rRange.EndRow ); + } + catch( Exception& ) + { + } + return xRange; +} + +Reference< XSheetCellRanges > WorksheetData::getCellRangeList( const ApiCellRangeList& rRanges ) const +{ + Reference< XSheetCellRanges > xRanges; + if( mxSheet.is() && !rRanges.empty() ) try + { + Reference< XMultiServiceFactory > xFactory( getDocument(), UNO_QUERY_THROW ); + xRanges.set( xFactory->createInstance( maSheetCellRanges ), UNO_QUERY_THROW ); + Reference< XSheetCellRangeContainer > xRangeCont( xRanges, UNO_QUERY_THROW ); + xRangeCont->addRangeAddresses( ContainerHelper::vectorToSequence( rRanges ), sal_False ); + } + catch( Exception& ) + { + } + return xRanges; +} + +Reference< XCellRange > WorksheetData::getColumn( sal_Int32 nCol ) const +{ + Reference< XCellRange > xColumn; + try + { + Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW ); + Reference< XTableColumns > xColumns = xColRowRange->getColumns(); + if( xColumns.is() ) + xColumn.set( xColumns->getByIndex( nCol ), UNO_QUERY ); + } + catch( Exception& ) + { + } + return xColumn; +} + +Reference< XCellRange > WorksheetData::getRow( sal_Int32 nRow ) const +{ + Reference< XCellRange > xRow; + try + { + Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW ); + Reference< XTableRows > xRows = xColRowRange->getRows(); + xRow.set( xRows->getByIndex( nRow ), UNO_QUERY ); + } + catch( Exception& ) + { + } + return xRow; +} + +Reference< XTableColumns > WorksheetData::getColumns( sal_Int32 nFirstCol, sal_Int32 nLastCol ) const +{ + Reference< XTableColumns > xColumns; + nLastCol = ::std::min( nLastCol, mrMaxApiPos.Column ); + if( (0 <= nFirstCol) && (nFirstCol <= nLastCol) ) + { + Reference< XColumnRowRange > xRange( getCellRange( CellRangeAddress( mnSheet, nFirstCol, 0, nLastCol, 0 ) ), UNO_QUERY ); + if( xRange.is() ) + xColumns = xRange->getColumns(); + } + return xColumns; +} + +Reference< XTableRows > WorksheetData::getRows( sal_Int32 nFirstRow, sal_Int32 nLastRow ) const +{ + Reference< XTableRows > xRows; + nLastRow = ::std::min( nLastRow, mrMaxApiPos.Row ); + if( (0 <= nFirstRow) && (nFirstRow <= nLastRow) ) + { + Reference< XColumnRowRange > xRange( getCellRange( CellRangeAddress( mnSheet, 0, nFirstRow, 0, nLastRow ) ), UNO_QUERY ); + if( xRange.is() ) + xRows = xRange->getRows(); + } + return xRows; +} + +void WorksheetData::setDimension( const CellRangeAddress& rRange ) +{ + maDimension = rRange; +} + +void WorksheetData::setCellFormat( const OoxCellData& rCellData ) +{ + if( rCellData.mxCell.is() && (rCellData.mnXfId >= 0) || (rCellData.mnNumFmtId >= 0) ) + { + // try to merge existing ranges and to write some formatting properties + if( !maXfIdRanges.empty() ) + { + // get row index of last inserted cell + sal_Int32 nLastRow = maXfIdRanges.rbegin()->second.maRange.StartRow; + // row changed - try to merge ranges of last row with existing ranges + if( rCellData.maAddress.Row != nLastRow ) + { + mergeXfIdRanges(); + // write format properties of all ranges above last row and remove them + XfIdRangeMap::iterator aIt = maXfIdRanges.begin(), aEnd = maXfIdRanges.end(); + while( aIt != aEnd ) + { + if( aIt->second.maRange.EndRow < nLastRow ) + { + writeXfIdRangeProperties( aIt->second ); + maXfIdRanges.erase( aIt++ ); + } + else + ++aIt; + } + } + } + + // try to expand last existing range, or create new range entry + if( maXfIdRanges.empty() || !maXfIdRanges.rbegin()->second.tryExpand( rCellData ) ) + maXfIdRanges[ RowColKey( rCellData.maAddress.Row, rCellData.maAddress.Column ) ].set( rCellData ); + + // update merged ranges for 'center across selection' and 'fill' + if( const Xf* pXf = getStyles().getCellXf( rCellData.mnXfId ).get() ) + { + sal_Int32 nHorAlign = pXf->getAlignment().getOoxData().mnHorAlign; + if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) ) + { + /* start new merged range, if cell is not empty (#108781#), + or try to expand last range with empty cell */ + if( rCellData.mnCellType != XML_TOKEN_INVALID ) + maCenterFillRanges.push_back( MergedRange( rCellData.maAddress, nHorAlign ) ); + else if( !maCenterFillRanges.empty() ) + maCenterFillRanges.rbegin()->tryExpand( rCellData.maAddress, nHorAlign ); + } + } + } +} + +void WorksheetData::setMergedRange( const CellRangeAddress& rRange ) +{ + maMergedRanges.push_back( MergedRange( rRange ) ); +} + +void WorksheetData::setPageBreak( const OoxPageBreakData& rData, bool bRowBreak ) +{ + if( rData.mbManual && (rData.mnColRow > 0) ) + { + PropertySet aPropSet( bRowBreak ? getRow( rData.mnColRow ) : getColumn( rData.mnColRow ) ); + aPropSet.setProperty( maPageBreakProp, true ); + } +} + +void WorksheetData::setHyperlink( const OoxHyperlinkData& rHyperlink ) +{ + maHyperlinks.push_back( rHyperlink ); +} + +void WorksheetData::setValidation( const OoxValidationData& rValData ) +{ + maValidations.push_back( rValData ); +} + +void WorksheetData::setBaseColumnWidth( sal_Int32 nWidth ) +{ + // do not modify width, if setDefaultColumnWidth() has been used + if( !mbHasDefWidth && (nWidth > 0) ) + { + /* #i3006# add 5 pixels padding to the width, assuming 1 pixel = + 1/96 inch. => 5/96 inch == 1.32 mm. */ + const UnitConverter& rUnitConv = getUnitConverter(); + maDefColData.mfWidth = rUnitConv.calcDigitsFromMm100( rUnitConv.calcMm100FromDigits( nWidth ) + 132 ); + } +} + +void WorksheetData::setDefaultColumnWidth( double fWidth ) +{ + // overrides a width set with setBaseColumnWidth() + if( fWidth > 0.0 ) + { + maDefColData.mfWidth = fWidth; + mbHasDefWidth = true; + } +} + +void WorksheetData::setColumnData( const OoxColumnData& rData ) +{ + // convert 1-based OOX column indexes to 0-based API column indexes + sal_Int32 nFirstCol = rData.mnFirstCol - 1; + sal_Int32 nLastCol = rData.mnLastCol - 1; + if( (0 <= nFirstCol) && (nFirstCol <= mrMaxApiPos.Column) ) + { + // set column formatting directly, nLastCol is checked inside the function + convertColumnFormat( nFirstCol, nLastCol, rData.mnXfId ); + // expand last entry or add new entry + if( maColDatas.empty() || !maColDatas.rbegin()->second.tryExpand( rData ) ) + maColDatas[ nFirstCol ] = rData; + } +} + +void WorksheetData::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom ) +{ + maDefRowData.mfHeight = fHeight; + maDefRowData.mbCustomHeight = bCustomHeight; + maDefRowData.mbHidden = bHidden; + maDefRowData.mbThickTop = bThickTop; + maDefRowData.mbThickBottom = bThickBottom; +} + +void WorksheetData::setRowData( const OoxRowData& rData ) +{ + // convert 1-based OOX row indexes to 0-based API row indexes + sal_Int32 nFirstRow = rData.mnFirstRow - 1; + sal_Int32 nLastRow = rData.mnLastRow - 1; + if( (0 <= nFirstRow) && (nFirstRow <= mrMaxApiPos.Row) ) + { + // set row formatting directly + if( rData.mbCustomFormat ) + convertRowFormat( nFirstRow, nLastRow, rData.mnXfId ); + // expand last entry or add new entry + if( maRowDatas.empty() || !maRowDatas.rbegin()->second.tryExpand( rData ) ) + maRowDatas[ nFirstRow ] = rData; + } + lclUpdateProgressBar( mxRowProgress, maDimension, nLastRow ); +} + +void WorksheetData::convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId ) +{ + CellRangeAddress aRange( mnSheet, nFirstCol, 0, nLastCol, mrMaxApiPos.Row ); + if( getAddressConverter().validateCellRange( aRange, false ) ) + { + PropertySet aPropSet( getCellRange( aRange ) ); + getStyles().writeCellXfToPropertySet( aPropSet, nXfId ); + } +} + +void WorksheetData::convertRowFormat( sal_Int32 nFirstRow, sal_Int32 nLastRow, sal_Int32 nXfId ) +{ + CellRangeAddress aRange( mnSheet, 0, nFirstRow, mrMaxApiPos.Column, nLastRow ); + if( getAddressConverter().validateCellRange( aRange, false ) ) + { + PropertySet aPropSet( getCellRange( aRange ) ); + getStyles().writeCellXfToPropertySet( aPropSet, nXfId ); + } +} + +void WorksheetData::initializeWorksheetImport() +{ +#if OOX_XLS_USE_DEFAULT_STYLE +#else + // set default cell style for unused cells + PropertySet aPropSet( mxSheet ); + aPropSet.setProperty( CREATE_OUSTRING( "CellStyle" ), getStyles().getDefaultStyleName() ); +#endif +} + +void WorksheetData::finalizeWorksheetImport() +{ + finalizeXfIdRanges(); + finalizeHyperlinkRanges(); + finalizeValidationRanges(); + finalizeMergedRanges(); + maCondFormats.finalizeImport(); + maPageSett.finalizeImport(); + maSheetViewSett.finalizeImport(); + convertColumns(); + convertRows(); +} + +// private -------------------------------------------------------------------- + +void WorksheetData::XfIdRange::set( const OoxCellData& rCellData ) +{ + maRange.Sheet = rCellData.maAddress.Sheet; + maRange.StartColumn = maRange.EndColumn = rCellData.maAddress.Column; + maRange.StartRow = maRange.EndRow = rCellData.maAddress.Row; + mnXfId = rCellData.mnXfId; + mnNumFmtId = rCellData.mnNumFmtId; +} + +bool WorksheetData::XfIdRange::tryExpand( const OoxCellData& rCellData ) +{ + if( (mnXfId == rCellData.mnXfId) && (mnNumFmtId == rCellData.mnNumFmtId) && + (maRange.StartRow == rCellData.maAddress.Row) && + (maRange.EndRow == rCellData.maAddress.Row) && + (maRange.EndColumn + 1 == rCellData.maAddress.Column) ) + { + ++maRange.EndColumn; + return true; + } + return false; +} + +bool WorksheetData::XfIdRange::tryMerge( const XfIdRange& rXfIdRange ) +{ + if( (mnXfId == rXfIdRange.mnXfId) && + (mnNumFmtId == rXfIdRange.mnNumFmtId) && + (maRange.EndRow + 1 == rXfIdRange.maRange.StartRow) && + (maRange.StartColumn == rXfIdRange.maRange.StartColumn) && + (maRange.EndColumn == rXfIdRange.maRange.EndColumn) ) + { + maRange.EndRow = rXfIdRange.maRange.EndRow; + return true; + } + return false; +} + + +WorksheetData::MergedRange::MergedRange( const CellRangeAddress& rRange ) : + maRange( rRange ), + mnHorAlign( XML_TOKEN_INVALID ) +{ +} + +WorksheetData::MergedRange::MergedRange( const CellAddress& rAddress, sal_Int32 nHorAlign ) : + maRange( rAddress.Sheet, rAddress.Column, rAddress.Row, rAddress.Column, rAddress.Row ), + mnHorAlign( nHorAlign ) +{ +} + +bool WorksheetData::MergedRange::tryExpand( const CellAddress& rAddress, sal_Int32 nHorAlign ) +{ + if( (mnHorAlign == nHorAlign) && (maRange.StartRow == rAddress.Row) && + (maRange.EndRow == rAddress.Row) && (maRange.EndColumn + 1 == rAddress.Column) ) + { + ++maRange.EndColumn; + return true; + } + return false; +} + +void WorksheetData::writeXfIdRangeProperties( const XfIdRange& rXfIdRange ) const +{ + StylesBuffer& rStyles = getStyles(); + PropertySet aPropSet( getCellRange( rXfIdRange.maRange ) ); + if( rXfIdRange.mnXfId >= 0 ) + rStyles.writeCellXfToPropertySet( aPropSet, rXfIdRange.mnXfId ); + if( rXfIdRange.mnNumFmtId >= 0 ) + rStyles.writeNumFmtToPropertySet( aPropSet, rXfIdRange.mnNumFmtId ); +} + +void WorksheetData::mergeXfIdRanges() +{ + if( !maXfIdRanges.empty() ) + { + // get row index of last range + sal_Int32 nLastRow = maXfIdRanges.rbegin()->second.maRange.StartRow; + // process all ranges located in the same row of the last range + XfIdRangeMap::iterator aMergeIt = maXfIdRanges.end(); + while( (aMergeIt != maXfIdRanges.begin()) && ((--aMergeIt)->second.maRange.StartRow == nLastRow) ) + { + const XfIdRange& rMergeXfIdRange = aMergeIt->second; + // try to find a range that can be merged with rMergeRange + bool bFound = false; + for( XfIdRangeMap::iterator aIt = maXfIdRanges.begin(); !bFound && (aIt != aMergeIt); ++aIt ) + if( (bFound = aIt->second.tryMerge( rMergeXfIdRange )) == true ) + maXfIdRanges.erase( aMergeIt++ ); + } + } +} + +void WorksheetData::finalizeXfIdRanges() +{ + // try to merge remaining inserted ranges + mergeXfIdRanges(); + // write all formatting + sal_Int32 nLastRow = -1; + for( XfIdRangeMap::const_iterator aIt = maXfIdRanges.begin(), aEnd = maXfIdRanges.end(); aIt != aEnd; ++aIt ) + { + writeXfIdRangeProperties( aIt->second ); + if( aIt->first.first > nLastRow ) + lclUpdateProgressBar( mxFinalProgress, maDimension, nLastRow = aIt->first.first ); + } +} + +void WorksheetData::finalizeHyperlinkRanges() const +{ + for( OoxHyperlinkList::const_iterator aIt = maHyperlinks.begin(), aEnd = maHyperlinks.end(); aIt != aEnd; ++aIt ) + { + OUStringBuffer aUrlBuffer( getBaseFilter().getAbsoluteUrl( aIt->maTarget ) ); + if( aIt->maLocation.getLength() > 0 ) + aUrlBuffer.append( sal_Unicode( '#' ) ).append( aIt->maLocation ); + OUString aUrl = aUrlBuffer.makeStringAndClear(); + if( aUrl.getLength() > 0 ) + { + // convert '#SheetName!A1' to '#SheetName.A1' + if( aUrl[ 0 ] == '#' ) + { + sal_Int32 nSepPos = aUrl.lastIndexOf( '!' ); + if( nSepPos > 0 ) + { + // replace the exclamation mark with a period + aUrl = aUrl.replaceAt( nSepPos, 1, OUString( sal_Unicode( '.' ) ) ); + // #i66592# convert renamed sheets + bool bQuotedName = (nSepPos > 3) && (aUrl[ 1 ] == '\'') && (aUrl[ nSepPos - 1 ] == '\''); + sal_Int32 nNamePos = bQuotedName ? 2 : 1; + sal_Int32 nNameLen = nSepPos - (bQuotedName ? 3 : 1); + OUString aSheetName = aUrl.copy( nNamePos, nNameLen ); + OUString aFinalName = getWorksheets().getFinalSheetName( aSheetName ); + if( aFinalName.getLength() > 0 ) + aUrl = aUrl.replaceAt( nNamePos, nNameLen, aFinalName ); + } + } + + // try to insert URL into each cell of the range + for( CellAddress aAddress( mnSheet, aIt->maRange.StartColumn, aIt->maRange.StartRow ); aAddress.Row <= aIt->maRange.EndRow; ++aAddress.Row ) + for( aAddress.Column = aIt->maRange.StartColumn; aAddress.Column <= aIt->maRange.EndColumn; ++aAddress.Column ) + finalizeHyperlink( aAddress, aUrl ); + } + } +} + +void WorksheetData::finalizeHyperlink( const CellAddress& rAddress, const OUString& rUrl ) const +{ + Reference< XMultiServiceFactory > xFactory( getDocument(), UNO_QUERY ); + Reference< XCell > xCell = getCell( rAddress ); + Reference< XText > xText( xCell, UNO_QUERY ); + // hyperlinks only supported in text cells + if( xFactory.is() && xCell.is() && (xCell->getType() == ::com::sun::star::table::CellContentType_TEXT) && xText.is() ) + { + // create a URL field object and set its properties + Reference< XTextContent > xUrlField( xFactory->createInstance( maUrlTextField ), UNO_QUERY ); + OSL_ENSURE( xUrlField.is(), "WorksheetData::finalizeHyperlink - cannot create text field" ); + if( xUrlField.is() ) + { + // properties of the URL field + PropertySet aPropSet( xUrlField ); + aPropSet.setProperty( maUrlProp, rUrl ); + aPropSet.setProperty( maReprProp, xText->getString() ); + try + { + // insert the field into the cell + xText->setString( OUString() ); + Reference< XTextRange > xRange( xText->createTextCursor(), UNO_QUERY_THROW ); + xText->insertTextContent( xRange, xUrlField, sal_False ); + } + catch( const Exception& ) + { + OSL_ENSURE( false, "WorksheetData::finalizeHyperlink - cannot insert text field" ); + } + } + } +} + +void WorksheetData::finalizeValidationRanges() const +{ + ValidationPropertyHelper& rPropHelper = getValidationPropertyHelper(); + for( OoxValidationList::const_iterator aIt = maValidations.begin(), aEnd = maValidations.end(); aIt != aEnd; ++aIt ) + { + PropertySet aPropSet( getCellRangeList( aIt->maRanges ) ); + rPropHelper.writeValidationProperties( aPropSet, *aIt ); + } +} + +void WorksheetData::finalizeMergedRanges() const +{ + MergedRangeList::const_iterator aIt, aEnd; + for( aIt = maMergedRanges.begin(), aEnd = maMergedRanges.end(); aIt != aEnd; ++aIt ) + finalizeMergedRange( aIt->maRange ); + for( aIt = maCenterFillRanges.begin(), aEnd = maCenterFillRanges.end(); aIt != aEnd; ++aIt ) + finalizeMergedRange( aIt->maRange ); +} + +void WorksheetData::finalizeMergedRange( const CellRangeAddress& rRange ) const +{ + bool bMultiCol = rRange.StartColumn < rRange.EndColumn; + bool bMultiRow = rRange.StartRow < rRange.EndRow; + + if( bMultiCol || bMultiRow ) try + { + // merge the cell range + Reference< XMergeable > xMerge( getCellRange( rRange ), UNO_QUERY_THROW ); + xMerge->merge( sal_True ); + + // if merging this range worked (no overlapping merged ranges), update cell borders + PropertySet aTopLeft( getCell( CellAddress( mnSheet, rRange.StartColumn, rRange.StartRow ) ) ); + + // copy right border of top-right cell to right border of top-left cell + if( bMultiCol ) + { + PropertySet aTopRight( getCell( CellAddress( mnSheet, rRange.EndColumn, rRange.StartRow ) ) ); + BorderLine aLine; + if( aTopRight.getProperty( aLine, maRightBorderProp ) ) + aTopLeft.setProperty( maRightBorderProp, aLine ); + } + + // copy bottom border of bottom-left cell to bottom border of top-left cell + if( bMultiRow ) + { + PropertySet aBottomLeft( getCell( CellAddress( mnSheet, rRange.StartColumn, rRange.EndRow ) ) ); + BorderLine aLine; + if( aBottomLeft.getProperty( aLine, maBottomBorderProp ) ) + aTopLeft.setProperty( maBottomBorderProp, aLine ); + } + } + catch( Exception& ) + { + } +} + +void WorksheetData::convertColumns() +{ + sal_Int32 nNextCol = 0; + sal_Int32 nMaxCol = mrMaxApiPos.Column; + // stores first grouped column index for each level + OutlineLevelVec aColLevels; + + for( OoxColumnDataMap::const_iterator aIt = maColDatas.begin(), aEnd = maColDatas.end(); aIt != aEnd; ++aIt ) + { + // convert 1-based OOX column indexes to 0-based API column indexes + sal_Int32 nFirstCol = ::std::max( aIt->second.mnFirstCol - 1, nNextCol ); + sal_Int32 nLastCol = ::std::min( aIt->second.mnLastCol - 1, nMaxCol ); + + // process gap between two column datas, use default column data + if( nNextCol < nFirstCol ) + convertColumns( aColLevels, nNextCol, nFirstCol - 1, maDefColData ); + // process the column data + convertColumns( aColLevels, nFirstCol, nLastCol, aIt->second ); + + // cache next column to be processed + nNextCol = nLastCol + 1; + } + + // remaining default columns to end of sheet + convertColumns( aColLevels, nNextCol, nMaxCol, maDefColData ); + // close remaining column outlines spanning to end of sheet + convertOutlines( aColLevels, nMaxCol + 1, 0, false, false ); +} + +void WorksheetData::convertColumns( OutlineLevelVec& orColLevels, + sal_Int32 nFirstCol, sal_Int32 nLastCol, const OoxColumnData& rData ) +{ + Reference< XTableColumns > xColumns = getColumns( nFirstCol, nLastCol ); + if( xColumns.is() ) + { + PropertySet aPropSet( xColumns ); + // column width: convert 'number of characters' to column width in 1/100 mm + sal_Int32 nWidth = getUnitConverter().calcMm100FromDigits( rData.mfWidth ); + if( nWidth > 0 ) + aPropSet.setProperty( maWidthProp, nWidth ); + // hidden columns: TODO: #108683# hide columns later? + if( rData.mbHidden ) + aPropSet.setProperty( maVisibleProp, false ); + } + // outline settings for this column range + convertOutlines( orColLevels, nFirstCol, rData.mnLevel, rData.mbCollapsed, false ); +} + +void WorksheetData::convertRows() +{ + sal_Int32 nNextRow = 0; + sal_Int32 nMaxRow = mrMaxApiPos.Row; + // stores first grouped row index for each level + OutlineLevelVec aRowLevels; + + for( OoxRowDataMap::const_iterator aIt = maRowDatas.begin(), aEnd = maRowDatas.end(); aIt != aEnd; ++aIt ) + { + // convert 1-based OOX row indexes to 0-based API row indexes + sal_Int32 nFirstRow = ::std::max( aIt->second.mnFirstRow - 1, nNextRow ); + sal_Int32 nLastRow = ::std::min( aIt->second.mnLastRow - 1, nMaxRow ); + + // process gap between two row datas, use default row data + if( nNextRow < nFirstRow ) + convertRows( aRowLevels, nNextRow, nFirstRow - 1, maDefRowData ); + // process the row data + convertRows( aRowLevels, nFirstRow, nLastRow, aIt->second, maDefRowData.mfHeight ); + + // cache next row to be processed + nNextRow = nLastRow + 1; + } + + // remaining default rows to end of sheet + convertRows( aRowLevels, nNextRow, nMaxRow, maDefRowData ); + // close remaining row outlines spanning to end of sheet + convertOutlines( aRowLevels, nMaxRow + 1, 0, false, true ); +} + +void WorksheetData::convertRows( OutlineLevelVec& orRowLevels, + sal_Int32 nFirstRow, sal_Int32 nLastRow, const OoxRowData& rData, double fDefHeight ) +{ + Reference< XTableRows > xRows = getRows( nFirstRow, nLastRow ); + if( xRows.is() ) + { + PropertySet aPropSet( xRows ); + /* Row height: + - convert points to row height in 1/100 mm + - ignore rData.mbCustomHeight, Excel does not set optimal height + correctly, if a merged cell contains multi-line text. + */ + double fHeight = (rData.mfHeight >= 0.0) ? rData.mfHeight : fDefHeight; + sal_Int32 nHeight = getUnitConverter().calcMm100FromPoints( fHeight ); + if( nHeight > 0 ) + aPropSet.setProperty( maHeightProp, nHeight ); + // hidden rows: TODO: #108683# hide rows later? + if( rData.mbHidden ) + aPropSet.setProperty( maVisibleProp, false ); + } + // outline settings for this row range + convertOutlines( orRowLevels, nFirstRow, rData.mnLevel, rData.mbCollapsed, true ); +} + +void WorksheetData::convertOutlines( OutlineLevelVec& orLevels, + sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows ) +{ + /* It is ensured from caller functions, that this function is called + without any gaps between the processed column or row ranges. */ + + OSL_ENSURE( nLevel >= 0, "WorksheetData::convertOutlines - negative outline level" ); + nLevel = ::std::max< sal_Int32 >( nLevel, 0 ); + + sal_Int32 nSize = orLevels.size(); + if( nSize < nLevel ) + { + // Outline level increased. Push the begin column position. + for( sal_Int32 nIndex = nSize; nIndex < nLevel; ++nIndex ) + orLevels.push_back( nColRow ); + } + else if( nLevel < nSize ) + { + // Outline level decreased. Pop them all out. + for( sal_Int32 nIndex = nLevel; nIndex < nSize; ++nIndex ) + { + sal_Int32 nFirstInLevel = orLevels.back(); + orLevels.pop_back(); + groupColumnsOrRows( nFirstInLevel, nColRow - 1, bCollapsed, bRows ); + bCollapsed = false; // collapse only once + } + } +} + +void WorksheetData::groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapse, bool bRows ) +{ + try + { + Reference< XSheetOutline > xOutline( mxSheet, UNO_QUERY_THROW ); + if( bRows ) + { + CellRangeAddress aRange( mnSheet, 0, nFirstColRow, 0, nLastColRow ); + xOutline->group( aRange, ::com::sun::star::table::TableOrientation_ROWS ); + if( bCollapse ) + xOutline->hideDetail( aRange ); + } + else + { + CellRangeAddress aRange( mnSheet, nFirstColRow, 0, nLastColRow, 0 ); + xOutline->group( aRange, ::com::sun::star::table::TableOrientation_COLUMNS ); + if( bCollapse ) + xOutline->hideDetail( aRange ); + } + } + catch( Exception& ) + { + } +} + +// ============================================================================ +// ============================================================================ + +WorksheetHelper::WorksheetHelper( WorksheetData& rSheetData ) : + WorkbookHelper( rSheetData ), + mrSheetData( rSheetData ) +{ +} + +WorksheetType WorksheetHelper::getSheetType() const +{ + return mrSheetData.getSheetType(); +} + +sal_Int16 WorksheetHelper::getSheetIndex() const +{ + return mrSheetData.getSheetIndex(); +} + +const Reference< XSpreadsheet >& WorksheetHelper::getXSpreadsheet() const +{ + return mrSheetData.getXSpreadsheet(); +} + +Reference< XCell > WorksheetHelper::getCell( const CellAddress& rAddress ) const +{ + return mrSheetData.getCell( rAddress ); +} + +Reference< XCell > WorksheetHelper::getCell( const OUString& rAddressStr, CellAddress* opAddress ) const +{ + CellAddress aAddress; + if( getAddressConverter().convertToCellAddress( aAddress, rAddressStr, mrSheetData.getSheetIndex(), true ) ) + { + if( opAddress ) *opAddress = aAddress; + return mrSheetData.getCell( aAddress ); + } + return Reference< XCell >(); +} + +Reference< XCell > WorksheetHelper::getCell( const BinAddress& rBinAddress, CellAddress* opAddress ) const +{ + CellAddress aAddress; + if( getAddressConverter().convertToCellAddress( aAddress, rBinAddress, mrSheetData.getSheetIndex(), true ) ) + { + if( opAddress ) *opAddress = aAddress; + return mrSheetData.getCell( aAddress ); + } + return Reference< XCell >(); +} + +Reference< XCellRange > WorksheetHelper::getCellRange( const CellRangeAddress& rRange ) const +{ + return mrSheetData.getCellRange( rRange ); +} + +Reference< XCellRange > WorksheetHelper::getCellRange( const OUString& rRangeStr, CellRangeAddress* opRange ) const +{ + CellRangeAddress aRange; + if( getAddressConverter().convertToCellRange( aRange, rRangeStr, mrSheetData.getSheetIndex(), true ) ) + { + if( opRange ) *opRange = aRange; + return mrSheetData.getCellRange( aRange ); + } + return Reference< XCellRange >(); +} + +Reference< XCellRange > WorksheetHelper::getCellRange( const BinRange& rBinRange, CellRangeAddress* opRange ) const +{ + CellRangeAddress aRange; + if( getAddressConverter().convertToCellRange( aRange, rBinRange, mrSheetData.getSheetIndex(), true ) ) + { + if( opRange ) *opRange = aRange; + return mrSheetData.getCellRange( aRange ); + } + return Reference< XCellRange >(); +} + +Reference< XSheetCellRanges > WorksheetHelper::getCellRangeList( const ApiCellRangeList& rRanges ) const +{ + return mrSheetData.getCellRangeList( rRanges ); +} + +Reference< XSheetCellRanges > WorksheetHelper::getCellRangeList( + const OUString& rRangesStr, ApiCellRangeList* opRanges ) const +{ + ApiCellRangeList aRanges; + getAddressConverter().convertToCellRangeList( aRanges, rRangesStr, mrSheetData.getSheetIndex(), true ); + if( opRanges ) *opRanges = aRanges; + return mrSheetData.getCellRangeList( aRanges ); +} + +Reference< XSheetCellRanges > WorksheetHelper::getCellRangeList( + const BinRangeList& rBinRanges, ApiCellRangeList* opRanges ) const +{ + ApiCellRangeList aRanges; + getAddressConverter().convertToCellRangeList( aRanges, rBinRanges, mrSheetData.getSheetIndex(), true ); + if( opRanges ) *opRanges = aRanges; + return mrSheetData.getCellRangeList( aRanges ); +} + +CellAddress WorksheetHelper::getCellAddress( const Reference< XCell >& rxCell ) +{ + CellAddress aAddress; + Reference< XCellAddressable > xAddressable( rxCell, UNO_QUERY ); + OSL_ENSURE( xAddressable.is(), "WorksheetHelper::getCellAddress - cell reference not addressable" ); + if( xAddressable.is() ) + aAddress = xAddressable->getCellAddress(); + return aAddress; +} + +CellRangeAddress WorksheetHelper::getRangeAddress( const Reference< XCellRange >& rxRange ) +{ + CellRangeAddress aRange; + Reference< XCellRangeAddressable > xAddressable( rxRange, UNO_QUERY ); + OSL_ENSURE( xAddressable.is(), "WorksheetHelper::getRangeAddress - cell range reference not addressable" ); + if( xAddressable.is() ) + aRange = xAddressable->getRangeAddress(); + return aRange; +} + +Reference< XCellRange > WorksheetHelper::getColumn( sal_Int32 nCol ) const +{ + return mrSheetData.getColumn( nCol ); +} + +Reference< XCellRange > WorksheetHelper::getRow( sal_Int32 nRow ) const +{ + return mrSheetData.getRow( nRow ); +} + +Reference< XTableColumns > WorksheetHelper::getColumns( sal_Int32 nFirstCol, sal_Int32 nLastCol ) const +{ + return mrSheetData.getColumns( nFirstCol, nLastCol ); +} + +Reference< XTableRows > WorksheetHelper::getRows( sal_Int32 nFirstRow, sal_Int32 nLastRow ) const +{ + return mrSheetData.getRows( nFirstRow, nLastRow ); +} + +WorksheetSettings& WorksheetHelper::getWorksheetSettings() const +{ + return mrSheetData.getWorksheetSettings(); +} + +SharedFormulaBuffer& WorksheetHelper::getSharedFormulas() const +{ + return mrSheetData.getSharedFormulas(); +} + +CondFormatBuffer& WorksheetHelper::getCondFormats() const +{ + return mrSheetData.getCondFormats(); +} + +PageSettings& WorksheetHelper::getPageSettings() const +{ + return mrSheetData.getPageSettings(); +} + +SheetViewSettings& WorksheetHelper::getSheetViewSettings() const +{ + return mrSheetData.getSheetViewSettings(); +} + +void WorksheetHelper::setEmptyStringCell( const Reference< XCell >& rxCell ) const +{ + OSL_ENSURE( rxCell.is(), "WorksheetHelper::setEmptyStringCell - missing cell interface" ); + rxCell->setFormula( mrSheetData.getEmptyStringFormula() ); +} + +void WorksheetHelper::setStringCell( const Reference< XCell >& rxCell, const OUString& rText, bool bEmptyStringAsFormula ) const +{ + if( bEmptyStringAsFormula && (rText.getLength() == 0) ) + { + setEmptyStringCell( rxCell ); + } + else + { + OSL_ENSURE( rxCell.is(), "WorksheetHelper::setStringCell - missing cell interface" ); + Reference< XText > xText( rxCell, UNO_QUERY ); + if( xText.is() ) + xText->setString( rText ); + } +} + +void WorksheetHelper::setSharedStringCell( const Reference< XCell >& rxCell, sal_Int32 nStringId, sal_Int32 nXfId ) const +{ + OSL_ENSURE( rxCell.is(), "WorksheetHelper::setSharedStringCell - missing cell interface" ); + getSharedStrings().convertString( Reference< XText >( rxCell, UNO_QUERY ), nStringId, nXfId ); +} + +void WorksheetHelper::setBooleanCell( const Reference< XCell >& rxCell, bool bValue ) const +{ + OSL_ENSURE( rxCell.is(), "WorksheetHelper::setBooleanCell - missing cell interface" ); + rxCell->setFormula( mrSheetData.getBooleanFormula( bValue ) ); +} + +void WorksheetHelper::setErrorCell( const Reference< XCell >& rxCell, const OUString& rErrorCode ) const +{ + setErrorCell( rxCell, getUnitConverter().calcBiffErrorCode( rErrorCode ) ); +} + +void WorksheetHelper::setErrorCell( const Reference< XCell >& rxCell, sal_uInt8 nErrorCode ) const +{ + Reference< XFormulaTokens > xTokens( rxCell, UNO_QUERY ); + OSL_ENSURE( xTokens.is(), "WorksheetHelper::setErrorCell - missing cell interface" ); + if( xTokens.is() ) + { + SimpleFormulaContext aContext( xTokens, false, false ); + getFormulaParser().convertErrorToFormula( aContext, nErrorCode ); + } +} + +void WorksheetHelper::setOoxCell( OoxCellData& orCellData, bool bEmptyStringAsFormula ) const +{ + OSL_ENSURE( orCellData.mxCell.is(), "WorksheetHelper::setCell - missing cell interface" ); + if( orCellData.mbHasValueStr ) switch( orCellData.mnCellType ) + { + case XML_b: + setBooleanCell( orCellData.mxCell, orCellData.maValueStr.toDouble() != 0.0 ); + // #108770# set 'Standard' number format for all Boolean cells + orCellData.mnNumFmtId = 0; + break; + case XML_n: + orCellData.mxCell->setValue( orCellData.maValueStr.toDouble() ); + break; + case XML_e: + setErrorCell( orCellData.mxCell, orCellData.maValueStr ); + break; + case XML_str: + setStringCell( orCellData.mxCell, orCellData.maValueStr, bEmptyStringAsFormula ); + break; + case XML_s: + setSharedStringCell( orCellData.mxCell, orCellData.maValueStr.toInt32(), orCellData.mnXfId ); + break; + } +} + +void WorksheetHelper::setDimension( const CellRangeAddress& rRange ) +{ + mrSheetData.setDimension( rRange ); +} + +void WorksheetHelper::setCellFormat( const OoxCellData& rCellData ) +{ + mrSheetData.setCellFormat( rCellData ); +} + +void WorksheetHelper::setMergedRange( const CellRangeAddress& rRange ) +{ + mrSheetData.setMergedRange( rRange ); +} + +void WorksheetHelper::setPageBreak( const OoxPageBreakData& rData, bool bRowBreak ) +{ + mrSheetData.setPageBreak( rData, bRowBreak ); +} + +void WorksheetHelper::setHyperlink( const OoxHyperlinkData& rHyperlink ) +{ + mrSheetData.setHyperlink( rHyperlink ); +} + +void WorksheetHelper::setValidation( const OoxValidationData& rValData ) +{ + mrSheetData.setValidation( rValData ); +} + +void WorksheetHelper::setTableOperation( const CellRangeAddress& rRange, const OoxDataTableData& rTableData ) const +{ + OSL_ENSURE( getAddressConverter().checkCellRange( rRange, false ), "WorksheetHelper::setTableOperation - invalid range" ); + bool bOk = false; + if( !rTableData.mbRef1Deleted && (rTableData.maRef1.getLength() > 0) && (rRange.StartColumn > 0) && (rRange.StartRow > 0) ) + { + CellRangeAddress aOpRange = rRange; + CellAddress aRef1, aRef2; + if( getAddressConverter().convertToCellAddress( aRef1, rTableData.maRef1, mrSheetData.getSheetIndex(), true ) ) try + { + if( rTableData.mb2dTable ) + { + if( !rTableData.mbRef2Deleted && getAddressConverter().convertToCellAddress( aRef2, rTableData.maRef2, mrSheetData.getSheetIndex(), true ) ) + { + // API call expects input values inside operation range + --aOpRange.StartColumn; + --aOpRange.StartRow; + // formula range is top-left cell of operation range + CellRangeAddress aFormulaRange( mrSheetData.getSheetIndex(), aOpRange.StartColumn, aOpRange.StartRow, aOpRange.StartColumn, aOpRange.StartRow ); + // set multiple operation + Reference< XMultipleOperation > xMultOp( mrSheetData.getCellRange( aOpRange ), UNO_QUERY_THROW ); + xMultOp->setTableOperation( aFormulaRange, ::com::sun::star::sheet::TableOperationMode_BOTH, aRef2, aRef1 ); + bOk = true; + } + } + else if( rTableData.mbRowTable ) + { + // formula range is column to the left of operation range + CellRangeAddress aFormulaRange( mrSheetData.getSheetIndex(), aOpRange.StartColumn - 1, aOpRange.StartRow, aOpRange.StartColumn - 1, aOpRange.EndRow ); + // API call expects input values (top row) inside operation range + --aOpRange.StartRow; + // set multiple operation + Reference< XMultipleOperation > xMultOp( mrSheetData.getCellRange( aOpRange ), UNO_QUERY_THROW ); + xMultOp->setTableOperation( aFormulaRange, ::com::sun::star::sheet::TableOperationMode_ROW, aRef1, aRef1 ); + bOk = true; + } + else + { + // formula range is row above operation range + CellRangeAddress aFormulaRange( mrSheetData.getSheetIndex(), aOpRange.StartColumn, aOpRange.StartRow - 1, aOpRange.EndColumn, aOpRange.StartRow - 1 ); + // API call expects input values (left column) inside operation range + --aOpRange.StartColumn; + // set multiple operation + Reference< XMultipleOperation > xMultOp( mrSheetData.getCellRange( aOpRange ), UNO_QUERY_THROW ); + xMultOp->setTableOperation( aFormulaRange, ::com::sun::star::sheet::TableOperationMode_COLUMN, aRef1, aRef1 ); + bOk = true; + } + } + catch( Exception& ) + { + } + } + + // on error: fill cell range with error codes + if( !bOk ) + { + for( CellAddress aPos( mrSheetData.getSheetIndex(), rRange.StartColumn, rRange.StartRow ); aPos.Row <= rRange.EndRow; ++aPos.Row ) + for( aPos.Column = rRange.StartColumn; aPos.Column <= rRange.EndColumn; ++aPos.Column ) + setErrorCell( mrSheetData.getCell( aPos ), BIFF_ERR_REF ); + } +} + +void WorksheetHelper::setLabelRanges( const ApiCellRangeList& rColRanges, const ApiCellRangeList& rRowRanges ) +{ + const CellAddress& rMaxPos = getAddressConverter().getMaxApiAddress(); + Reference< XLabelRanges > xLabelRanges; + PropertySet aPropSet( getXSpreadsheet() ); + + if( !rColRanges.empty() && aPropSet.getProperty( xLabelRanges, CREATE_OUSTRING( "ColumnLabelRanges" ) ) && xLabelRanges.is() ) + { + for( ApiCellRangeList::const_iterator aIt = rColRanges.begin(), aEnd = rColRanges.end(); aIt != aEnd; ++aIt ) + { + CellRangeAddress aDataRange = *aIt; + if( aDataRange.EndRow < rMaxPos.Row ) + { + aDataRange.StartRow = aDataRange.EndRow + 1; + aDataRange.EndRow = rMaxPos.Row; + } + else if( aDataRange.StartRow > 0 ) + { + aDataRange.EndRow = aDataRange.StartRow - 1; + aDataRange.StartRow = 0; + } + xLabelRanges->addNew( *aIt, aDataRange ); + } + } + + if( !rRowRanges.empty() && aPropSet.getProperty( xLabelRanges, CREATE_OUSTRING( "RowLabelRanges" ) ) && xLabelRanges.is() ) + { + for( ApiCellRangeList::const_iterator aIt = rRowRanges.begin(), aEnd = rRowRanges.end(); aIt != aEnd; ++aIt ) + { + CellRangeAddress aDataRange = *aIt; + if( aDataRange.EndColumn < rMaxPos.Column ) + { + aDataRange.StartColumn = aDataRange.EndColumn + 1; + aDataRange.EndColumn = rMaxPos.Column; + } + else if( aDataRange.StartColumn > 0 ) + { + aDataRange.EndColumn = aDataRange.StartColumn - 1; + aDataRange.StartColumn = 0; + } + xLabelRanges->addNew( *aIt, aDataRange ); + } + } +} + +void WorksheetHelper::setBaseColumnWidth( sal_Int32 nWidth ) +{ + mrSheetData.setBaseColumnWidth( nWidth ); +} + +void WorksheetHelper::setDefaultColumnWidth( double fWidth ) +{ + mrSheetData.setDefaultColumnWidth( fWidth ); +} + +void WorksheetHelper::setColumnData( const OoxColumnData& rData ) +{ + mrSheetData.setColumnData( rData ); +} + +void WorksheetHelper::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom ) +{ + mrSheetData.setDefaultRowSettings( fHeight, bCustomHeight, bHidden, bThickTop, bThickBottom ); +} + +void WorksheetHelper::setRowData( const OoxRowData& rData ) +{ + mrSheetData.setRowData( rData ); +} + +void WorksheetHelper::convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId ) +{ + mrSheetData.convertColumnFormat( nFirstCol, nLastCol, nXfId ); +} + +void WorksheetHelper::convertRowFormat( sal_Int32 nFirstRow, sal_Int32 nLastRow, sal_Int32 nXfId ) +{ + mrSheetData.convertRowFormat( nFirstRow, nLastRow, nXfId ); +} + +void WorksheetHelper::initializeWorksheetImport() +{ + mrSheetData.initializeWorksheetImport(); +} + +void WorksheetHelper::finalizeWorksheetImport() +{ + mrSheetData.finalizeWorksheetImport(); +} + +// ============================================================================ + +namespace prv { + +WorksheetDataOwner::WorksheetDataOwner( WorksheetDataRef xSheetData ) : + mxSheetData( xSheetData ) +{ +} + +WorksheetDataOwner::~WorksheetDataOwner() +{ +} + +} // namespace prv + +// ---------------------------------------------------------------------------- + +WorksheetHelperRoot::WorksheetHelperRoot( const WorkbookHelper& rHelper, ISegmentProgressBarRef xProgressBar, WorksheetType eSheetType, sal_Int32 nSheet ) : + prv::WorksheetDataOwner( prv::WorksheetDataRef( new WorksheetData( rHelper, xProgressBar, eSheetType, nSheet ) ) ), + WorksheetHelper( *mxSheetData ) +{ +} + +WorksheetHelperRoot::WorksheetHelperRoot( const WorksheetHelper& rHelper ) : + prv::WorksheetDataOwner( prv::WorksheetDataRef() ), + WorksheetHelper( rHelper ) +{ +} + +WorksheetHelperRoot::WorksheetHelperRoot( const WorksheetHelperRoot& rHelper ) : + prv::WorksheetDataOwner( rHelper.mxSheetData ), + WorksheetHelper( rHelper ) +{ +} + +bool WorksheetHelperRoot::isValidSheet() const +{ + return mxSheetData->isValidSheet(); +} + +// ============================================================================ +// ============================================================================ + +} // namespace xls +} // namespace oox + diff --git a/oox/source/xls/worksheetsettings.cxx b/oox/source/xls/worksheetsettings.cxx new file mode 100644 index 000000000000..d827fc1ff70d --- /dev/null +++ b/oox/source/xls/worksheetsettings.cxx @@ -0,0 +1,270 @@ +/************************************************************************* + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: worksheetsettings.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: rt $ $Date: 2008-01-17 08:06:10 $ + * + * The Contents of this file are made available subject to + * the terms of GNU Lesser General Public License Version 2.1. + * + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2005 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ************************************************************************/ + +#include "oox/xls/worksheetsettings.hxx" +#include <com/sun/star/util/XProtectable.hpp> +#include "oox/helper/attributelist.hxx" +#include "oox/helper/recordinputstream.hxx" +#include "oox/xls/biffinputstream.hxx" +#include "oox/xls/pagesettings.hxx" +#include "oox/xls/workbooksettings.hxx" + +using ::rtl::OUString; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::util::XProtectable; + +namespace oox { +namespace xls { + +// ============================================================================ + +namespace { + +const sal_uInt16 BIFF_SHEETPR_APPLYSTYLES = 0x0020; +const sal_uInt16 BIFF_SHEETPR_SYMBOLSBELOW = 0x0040; +const sal_uInt16 BIFF_SHEETPR_SYMBOLSRIGHT = 0x0080; +const sal_uInt16 BIFF_SHEETPR_FITTOPAGES = 0x0100; +const sal_uInt16 BIFF_SHEETPR_SKIPEXT = 0x0200; /// BIFF3-BIFF4 + +const sal_uInt16 BIFF_SHEETPROT_OBJECTS = 0x0001; +const sal_uInt16 BIFF_SHEETPROT_SCENARIOS = 0x0002; +const sal_uInt16 BIFF_SHEETPROT_FORMAT_CELLS = 0x0004; +const sal_uInt16 BIFF_SHEETPROT_FORMAT_COLUMNS = 0x0008; +const sal_uInt16 BIFF_SHEETPROT_FORMAT_ROWS = 0x0010; +const sal_uInt16 BIFF_SHEETPROT_INSERT_COLUMNS = 0x0020; +const sal_uInt16 BIFF_SHEETPROT_INSERT_ROWS = 0x0040; +const sal_uInt16 BIFF_SHEETPROT_INSERT_HLINKS = 0x0080; +const sal_uInt16 BIFF_SHEETPROT_DELETE_COLUMNS = 0x0100; +const sal_uInt16 BIFF_SHEETPROT_DELETE_ROWS = 0x0200; +const sal_uInt16 BIFF_SHEETPROT_SELECT_LOCKED = 0x0400; +const sal_uInt16 BIFF_SHEETPROT_SORT = 0x0800; +const sal_uInt16 BIFF_SHEETPROT_AUTOFILTER = 0x1000; +const sal_uInt16 BIFF_SHEETPROT_PIVOTTABLES = 0x2000; +const sal_uInt16 BIFF_SHEETPROT_SELECT_UNLOCKED = 0x4000; + +} // namespace + +// ============================================================================ + +OoxOutlinePrData::OoxOutlinePrData() : + mbApplyStyles( false ), + mbSummaryBelow( true ), + mbSummaryRight( true ) +{ +} + +// ============================================================================ + +OoxSheetProtectionData::OoxSheetProtectionData() : + mnPasswordHash( 0 ), + mbSheet( false ), + mbObjects( false ), + mbScenarios( false ), + mbFormatCells( true ), + mbFormatColumns( true ), + mbFormatRows( true ), + mbInsertColumns( true ), + mbInsertRows( true ), + mbInsertHyperlinks( true ), + mbDeleteColumns( true ), + mbDeleteRows( true ), + mbSelectLocked( false ), + mbSort( true ), + mbAutoFilter( true ), + mbPivotTables( true ), + mbSelectUnlocked( false ) +{ +} + +// ============================================================================ + +WorksheetSettings::WorksheetSettings( const WorksheetHelper& rHelper ) : + WorksheetHelper( rHelper ), + maPhoneticSett( rHelper ) +{ +} + +void WorksheetSettings::importOutlinePr( const AttributeList& rAttribs ) +{ + maOoxOutlineData.mbApplyStyles = rAttribs.getBool( XML_applyStyles, false ); + maOoxOutlineData.mbSummaryBelow = rAttribs.getBool( XML_summaryBelow, true ); + maOoxOutlineData.mbSummaryRight = rAttribs.getBool( XML_summaryRight, true ); +} + +void WorksheetSettings::importSheetProtection( const AttributeList& rAttribs ) +{ + sal_Int32 nHash = rAttribs.getHex( XML_password, 0 ); + OSL_ENSURE( (0 <= nHash) && (nHash <= SAL_MAX_UINT16), "WorksheetSettings::importSheetProtection - invalid password hash" ); + maOoxProtData.mnPasswordHash = static_cast< sal_uInt16 >( nHash ); + maOoxProtData.mbSheet = rAttribs.getBool( XML_sheet, false ); + maOoxProtData.mbObjects = rAttribs.getBool( XML_objects, false ); + maOoxProtData.mbScenarios = rAttribs.getBool( XML_scenarios, false ); + maOoxProtData.mbFormatCells = rAttribs.getBool( XML_formatCells, true ); + maOoxProtData.mbFormatColumns = rAttribs.getBool( XML_formatColumns, true ); + maOoxProtData.mbFormatRows = rAttribs.getBool( XML_formatRows, true ); + maOoxProtData.mbInsertColumns = rAttribs.getBool( XML_insertColumns, true ); + maOoxProtData.mbInsertRows = rAttribs.getBool( XML_insertRows, true ); + maOoxProtData.mbInsertHyperlinks = rAttribs.getBool( XML_insertHyperlinks, true ); + maOoxProtData.mbDeleteColumns = rAttribs.getBool( XML_deleteColumns, true ); + maOoxProtData.mbDeleteRows = rAttribs.getBool( XML_deleteRows, true ); + maOoxProtData.mbSelectLocked = rAttribs.getBool( XML_selectLockedCells, false ); + maOoxProtData.mbSort = rAttribs.getBool( XML_sort, true ); + maOoxProtData.mbAutoFilter = rAttribs.getBool( XML_autoFilter, true ); + maOoxProtData.mbPivotTables = rAttribs.getBool( XML_pivotTables, true ); + maOoxProtData.mbSelectUnlocked = rAttribs.getBool( XML_selectUnlockedCells, false ); +} + +void WorksheetSettings::importPhoneticPr( const AttributeList& rAttribs ) +{ + maPhoneticSett.importPhoneticPr( rAttribs ); +} + +void WorksheetSettings::importSheetPr( RecordInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags; + // outline settings, equal flags in BIFF and OOBIN + maOoxOutlineData.mbApplyStyles = getFlag( nFlags, BIFF_SHEETPR_APPLYSTYLES ); + maOoxOutlineData.mbSummaryRight = getFlag( nFlags, BIFF_SHEETPR_SYMBOLSRIGHT ); + maOoxOutlineData.mbSummaryBelow = getFlag( nFlags, BIFF_SHEETPR_SYMBOLSBELOW ); + /* Fit printout to width/height - for whatever reason, this flag is still + stored separated from the page settings */ + getPageSettings().setFitToPagesMode( getFlag( nFlags, BIFF_SHEETPR_FITTOPAGES ) ); +} + +void WorksheetSettings::importSheetProtection( RecordInputStream& rStrm ) +{ + rStrm >> maOoxProtData.mnPasswordHash; + // no flags field for all these boolean flags?!? + maOoxProtData.mbSheet = rStrm.readInt32() != 0; + maOoxProtData.mbObjects = rStrm.readInt32() != 0; + maOoxProtData.mbScenarios = rStrm.readInt32() != 0; + maOoxProtData.mbFormatCells = rStrm.readInt32() != 0; + maOoxProtData.mbFormatColumns = rStrm.readInt32() != 0; + maOoxProtData.mbFormatRows = rStrm.readInt32() != 0; + maOoxProtData.mbInsertColumns = rStrm.readInt32() != 0; + maOoxProtData.mbInsertRows = rStrm.readInt32() != 0; + maOoxProtData.mbInsertHyperlinks = rStrm.readInt32() != 0; + maOoxProtData.mbDeleteColumns = rStrm.readInt32() != 0; + maOoxProtData.mbDeleteRows = rStrm.readInt32() != 0; + maOoxProtData.mbSelectLocked = rStrm.readInt32() != 0; + maOoxProtData.mbSort = rStrm.readInt32() != 0; + maOoxProtData.mbAutoFilter = rStrm.readInt32() != 0; + maOoxProtData.mbPivotTables = rStrm.readInt32() != 0; + maOoxProtData.mbSelectUnlocked = rStrm.readInt32() != 0; +} + +void WorksheetSettings::importPhoneticPr( RecordInputStream& rStrm ) +{ + maPhoneticSett.importPhoneticPr( rStrm ); +} + +void WorksheetSettings::importSheetPr( BiffInputStream& rStrm ) +{ + sal_uInt16 nFlags; + rStrm >> nFlags; + // outline settings + maOoxOutlineData.mbApplyStyles = getFlag( nFlags, BIFF_SHEETPR_APPLYSTYLES ); + maOoxOutlineData.mbSummaryRight = getFlag( nFlags, BIFF_SHEETPR_SYMBOLSRIGHT ); + maOoxOutlineData.mbSummaryBelow = getFlag( nFlags, BIFF_SHEETPR_SYMBOLSBELOW ); + // fit printout to width/height + getPageSettings().setFitToPagesMode( getFlag( nFlags, BIFF_SHEETPR_FITTOPAGES ) ); + // save external linked values, in BIFF5-BIFF8 moved to BOOKBOOK record + if( getBiff() <= BIFF4 ) + getWorkbookSettings().setSaveExtLinkValues( !getFlag( nFlags, BIFF_SHEETPR_SKIPEXT ) ); +} + +void WorksheetSettings::importProtect( BiffInputStream& rStrm ) +{ + maOoxProtData.mbSheet = rStrm.readuInt16() != 0; +} + +void WorksheetSettings::importObjectProtect( BiffInputStream& rStrm ) +{ + maOoxProtData.mbObjects = rStrm.readuInt16() != 0; +} + +void WorksheetSettings::importScenProtect( BiffInputStream& rStrm ) +{ + maOoxProtData.mbScenarios = rStrm.readuInt16() != 0; +} + +void WorksheetSettings::importPassword( BiffInputStream& rStrm ) +{ + rStrm >> maOoxProtData.mnPasswordHash; +} + +void WorksheetSettings::importSheetProtection( BiffInputStream& rStrm ) +{ + sal_uInt16 nFlags = rStrm.skip( 19 ).readuInt16(); + // set flag means protection is disabled + maOoxProtData.mbObjects = !getFlag( nFlags, BIFF_SHEETPROT_OBJECTS ); + maOoxProtData.mbScenarios = !getFlag( nFlags, BIFF_SHEETPROT_SCENARIOS ); + maOoxProtData.mbFormatCells = !getFlag( nFlags, BIFF_SHEETPROT_FORMAT_CELLS ); + maOoxProtData.mbFormatColumns = !getFlag( nFlags, BIFF_SHEETPROT_FORMAT_COLUMNS ); + maOoxProtData.mbFormatRows = !getFlag( nFlags, BIFF_SHEETPROT_FORMAT_ROWS ); + maOoxProtData.mbInsertColumns = !getFlag( nFlags, BIFF_SHEETPROT_INSERT_COLUMNS ); + maOoxProtData.mbInsertRows = !getFlag( nFlags, BIFF_SHEETPROT_INSERT_ROWS ); + maOoxProtData.mbInsertHyperlinks = !getFlag( nFlags, BIFF_SHEETPROT_INSERT_HLINKS ); + maOoxProtData.mbDeleteColumns = !getFlag( nFlags, BIFF_SHEETPROT_DELETE_COLUMNS ); + maOoxProtData.mbDeleteRows = !getFlag( nFlags, BIFF_SHEETPROT_DELETE_ROWS ); + maOoxProtData.mbSelectLocked = !getFlag( nFlags, BIFF_SHEETPROT_SELECT_LOCKED ); + maOoxProtData.mbSort = !getFlag( nFlags, BIFF_SHEETPROT_SORT ); + maOoxProtData.mbAutoFilter = !getFlag( nFlags, BIFF_SHEETPROT_AUTOFILTER ); + maOoxProtData.mbPivotTables = !getFlag( nFlags, BIFF_SHEETPROT_PIVOTTABLES ); + maOoxProtData.mbSelectUnlocked = !getFlag( nFlags, BIFF_SHEETPROT_SELECT_UNLOCKED ); +} + +void WorksheetSettings::importPhoneticPr( BiffInputStream& rStrm ) +{ + maPhoneticSett.importPhoneticPr( rStrm ); +} + +void WorksheetSettings::finalizeImport() +{ + if( maOoxProtData.mbSheet ) + { + Reference< XProtectable > xProtectable( getXSpreadsheet(), UNO_QUERY ); + if( xProtectable.is() ) + xProtectable->protect( OUString() ); + } +} + +// ============================================================================ + +} // namespace xls +} // namespace oox + |