xref: /aoo41x/main/oox/source/xls/biffhelper.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "oox/xls/biffhelper.hxx"
29 
30 #include <rtl/math.hxx>
31 #include <rtl/tencinfo.h>
32 #include "oox/xls/biffinputstream.hxx"
33 #include "oox/xls/biffoutputstream.hxx"
34 #include "oox/xls/worksheethelper.hxx"
35 
36 namespace oox {
37 namespace xls {
38 
39 // ============================================================================
40 
41 using ::rtl::OUString;
42 using ::rtl::OUStringBuffer;
43 
44 // ============================================================================
45 
46 namespace {
47 
48 const sal_Int32 BIFF_RK_100FLAG             = 0x00000001;
49 const sal_Int32 BIFF_RK_INTFLAG             = 0x00000002;
50 const sal_Int32 BIFF_RK_VALUEMASK           = 0xFFFFFFFC;
51 
52 const sal_Int32 BITMAPFILEHEADER_SIZE       = 14;
53 const sal_Int32 BITMAPCOREHEADER_SIZE       = 12;
54 const sal_Int32 BITMAPINFOHEADER_SIZE       = 40;
55 
56 const sal_uInt16 BIFF_IMGDATA_WMF           = 2;
57 const sal_uInt16 BIFF_IMGDATA_DIB           = 9;
58 const sal_uInt16 BIFF_IMGDATA_NATIVE        = 14;
59 
60 // ----------------------------------------------------------------------------
61 
62 union DecodedDouble
63 {
64     double              mfValue;
65     sal_math_Double     maStruct;
66 
67     inline explicit     DecodedDouble() {}
68     inline explicit     DecodedDouble( double fValue ) : mfValue( fValue ) {}
69 };
70 
71 bool lclCalcRkFromDouble( sal_Int32& ornRkValue, const DecodedDouble& rDecDbl )
72 {
73     // double
74     if( (rDecDbl.maStruct.w32_parts.lsw == 0) && ((rDecDbl.maStruct.w32_parts.msw & 0x3) == 0) )
75     {
76         ornRkValue = static_cast< sal_Int32 >( rDecDbl.maStruct.w32_parts.msw );
77         return true;
78     }
79 
80     // integer
81     double fInt = 0.0;
82     double fFrac = modf( rDecDbl.mfValue, &fInt );
83     if( (fFrac == 0.0) && (-536870912.0 <= fInt) && (fInt <= 536870911.0) ) // 2^29
84     {
85         ornRkValue = static_cast< sal_Int32 >( fInt );
86         ornRkValue <<= 2;
87         ornRkValue |= BIFF_RK_INTFLAG;
88         return true;
89     }
90 
91     return false;
92 }
93 
94 bool lclCalcRkFromDouble( sal_Int32& ornRkValue, double fValue )
95 {
96     DecodedDouble aDecDbl( fValue );
97     if( lclCalcRkFromDouble( ornRkValue, aDecDbl ) )
98         return true;
99 
100     aDecDbl.mfValue *= 100.0;
101     if( lclCalcRkFromDouble( ornRkValue, aDecDbl ) )
102     {
103         ornRkValue |= BIFF_RK_100FLAG;
104         return true;
105     }
106 
107     return false;
108 }
109 
110 // ----------------------------------------------------------------------------
111 
112 void lclImportImgDataDib( StreamDataSequence& orDataSeq, BiffInputStream& rStrm, sal_Int32 nBytes, BiffType eBiff )
113 {
114     /*  The IMGDATA record for bitmap format contains a Windows DIB (a bitmap
115         file without the 'BITMAPFILEHEADER' header structure). Usually, the DIB
116         header consists of 12 bytes (called 'OS/2 V1 header' or
117         'BITMAPCOREHEADER', see http://en.wikipedia.org/wiki/BMP_file_format)
118         followed by the remaining pixel data, but the 'Windows V3' or
119         'BITMAPINFOHEADER' is also supported here. This function creates a
120         complete 'BMP file' that can be read by the OOo graphic provider used
121         to import graphic objects. For that, the BITMAPFILEHEADER has to be
122         inserted before the DIB data, and it has to contain the correct offset
123         to the pixel data. Currently, in real life there are only 24-bit and
124         32-bit DIBs (without color palette) in use. This code relies on this
125         fact and calculates the offset to the pixel data according to the size
126         of the DIB header.
127         Remaining tasks are (if really used somewhere):
128         - Support of DIBs with color palette,
129         - Support of 'Windows V4' and 'Windows V5' DIB header. */
130 
131     // read and check validity of DIB header
132     sal_Int64 nInStrmPos = rStrm.tell();
133     sal_Int32 nDibHdrSize = rStrm.readInt32();
134     sal_uInt16 nPlanes = 0, nDepth = 0;
135     switch( nDibHdrSize )
136     {
137         case BITMAPCOREHEADER_SIZE:
138             rStrm.skip( 4 );    // width/height as 16-bit integer
139             rStrm >> nPlanes >> nDepth;
140         break;
141         case BITMAPINFOHEADER_SIZE:
142             rStrm.skip( 8 );    // width/height as 32-bit integer
143             rStrm >> nPlanes >> nDepth;
144         break;
145     }
146     rStrm.seek( nInStrmPos );
147 
148     if( (nPlanes == 1) && ((nDepth == 24) || (nDepth == 32)) )
149     {
150         // allocate enough space for the BITMAPFILEHEADER and the DIB data
151         orDataSeq.realloc( BITMAPFILEHEADER_SIZE + nBytes );
152         SequenceOutputStream aOutStrm( orDataSeq );
153 
154         // write the BITMAPFILEHEADER of a regular BMP file
155         sal_Int32 nBmpSize = BITMAPFILEHEADER_SIZE + nBytes;
156         sal_Int32 nOffset = BITMAPFILEHEADER_SIZE + nDibHdrSize;
157         aOutStrm << sal_uInt16( 0x4D42 ) << nBmpSize << sal_Int32( 0 ) << nOffset;
158 
159         // copy the DIB header
160         rStrm.copyToStream( aOutStrm, nDibHdrSize );
161         nBytes -= nDibHdrSize;
162 
163         /*  Excel 3.x and Excel 4.x seem to write broken or out-dated DIB data.
164             Usually they write a BITMAPCOREHEADER containing width, height,
165             planes as usual. The pixel depth field is set to 32 bit (though
166             this is not allowed according to documentation). Between that
167             header and the actual pixel data, 3 unused bytes are inserted. This
168             does even confuse Excel 5.x and later, which cannot read the image
169             data correctly. */
170         if( (eBiff <= BIFF4) && (nDibHdrSize == BITMAPCOREHEADER_SIZE) && (nDepth == 32) )
171         {
172             // skip the dummy bytes in input stream
173             rStrm.skip( 3 );
174             nBytes -= 3;
175             // correct the total BMP file size in output stream
176             sal_Int64 nOutStrmPos = aOutStrm.tell();
177             aOutStrm.seek( 2 );
178             aOutStrm << sal_Int32( nBmpSize - 3 );
179             aOutStrm.seek( nOutStrmPos );
180         }
181 
182         // copy remaining pixel data to output stream
183         rStrm.copyToStream( aOutStrm, nBytes );
184     }
185     rStrm.seek( nInStrmPos + nBytes );
186 }
187 
188 } // namespace
189 
190 // ============================================================================
191 
192 // conversion -----------------------------------------------------------------
193 
194 /*static*/ double BiffHelper::calcDoubleFromRk( sal_Int32 nRkValue )
195 {
196     DecodedDouble aDecDbl( 0.0 );
197     if( getFlag( nRkValue, BIFF_RK_INTFLAG ) )
198     {
199         sal_Int32 nTemp = nRkValue >> 2;
200         setFlag< sal_Int32 >( nTemp, 0xE0000000, nRkValue < 0 );
201         aDecDbl.mfValue = nTemp;
202     }
203     else
204     {
205         aDecDbl.maStruct.w32_parts.msw = static_cast< sal_uInt32 >( nRkValue & BIFF_RK_VALUEMASK );
206     }
207 
208     if( getFlag( nRkValue, BIFF_RK_100FLAG ) )
209         aDecDbl.mfValue /= 100.0;
210 
211     return aDecDbl.mfValue;
212 }
213 
214 /*static*/ bool BiffHelper::calcRkFromDouble( sal_Int32& ornRkValue, double fValue )
215 {
216     if( lclCalcRkFromDouble( ornRkValue, fValue ) )
217         return true;
218 
219     if( lclCalcRkFromDouble( ornRkValue, fValue * 100 ) )
220     {
221         ornRkValue |= BIFF_RK_100FLAG;
222         return true;
223     }
224 
225     return false;
226 }
227 
228 /*static*/ double BiffHelper::calcDoubleFromError( sal_uInt8 nErrorCode )
229 {
230     sal_uInt16 nApiError = 0x7FFF;
231     switch( nErrorCode )
232     {
233         case BIFF_ERR_NULL:     nApiError = 521;    break;
234         case BIFF_ERR_DIV0:     nApiError = 532;    break;
235         case BIFF_ERR_VALUE:    nApiError = 519;    break;
236         case BIFF_ERR_REF:      nApiError = 524;    break;
237         case BIFF_ERR_NAME:     nApiError = 525;    break;
238         case BIFF_ERR_NUM:      nApiError = 503;    break;
239         case BIFF_ERR_NA:       nApiError = 0x7FFF; break;
240         default:    OSL_ENSURE( false, "BiffHelper::calcDoubleFromError - unknown error code" );
241     }
242     DecodedDouble aDecDbl;
243     ::rtl::math::setNan( &aDecDbl.mfValue );
244     aDecDbl.maStruct.nan_parts.fraction_lo = nApiError;
245     return aDecDbl.mfValue;
246 }
247 
248 /*static*/ rtl_TextEncoding BiffHelper::calcTextEncodingFromCodePage( sal_uInt16 nCodePage )
249 {
250     // some specials for BIFF
251     switch( nCodePage )
252     {
253         case 1200:  return RTL_TEXTENCODING_DONTKNOW;       // BIFF8 Unicode
254         case 32768: return RTL_TEXTENCODING_APPLE_ROMAN;
255         case 32769: return RTL_TEXTENCODING_MS_1252;        // BIFF2-BIFF3
256     }
257 
258     rtl_TextEncoding eTextEnc = rtl_getTextEncodingFromWindowsCodePage( nCodePage );
259     OSL_ENSURE( eTextEnc != RTL_TEXTENCODING_DONTKNOW, "BiffHelper::calcTextEncodingFromCodePage - unknown code page" );
260     return eTextEnc;
261 }
262 
263 /*static*/ sal_uInt16 BiffHelper::calcCodePageFromTextEncoding( rtl_TextEncoding eTextEnc )
264 {
265     sal_uInt32 nCodePage = rtl_getWindowsCodePageFromTextEncoding( eTextEnc );
266     OSL_ENSURE( (0 < nCodePage) && (nCodePage <= SAL_MAX_UINT16), "BiffHelper::calcCodePageFromTextEncoding - unknown text encoding" );
267     return static_cast< sal_uInt16 >( (nCodePage == 0) ? 1252 : nCodePage );
268 }
269 
270 // BIFF12 import --------------------------------------------------------------
271 
272 /*static*/ OUString BiffHelper::readString( SequenceInputStream& rStrm, bool b32BitLen, bool bAllowNulChars )
273 {
274     OUString aString;
275     if( !rStrm.isEof() )
276     {
277         sal_Int32 nCharCount = b32BitLen ? rStrm.readValue< sal_Int32 >() : rStrm.readValue< sal_Int16 >();
278         // string length -1 is often used to indicate a missing string
279         OSL_ENSURE( !rStrm.isEof() && (nCharCount >= -1), "BiffHelper::readString - invalid string length" );
280         if( !rStrm.isEof() && (nCharCount > 0) )
281         {
282             // SequenceInputStream always supports getRemaining()
283             nCharCount = ::std::min( nCharCount, static_cast< sal_Int32 >( rStrm.getRemaining() / 2 ) );
284             aString = rStrm.readUnicodeArray( nCharCount, bAllowNulChars );
285         }
286     }
287     return aString;
288 }
289 
290 // BIFF2-BIFF8 import ---------------------------------------------------------
291 
292 /*static*/ bool BiffHelper::isBofRecord( BiffInputStream& rStrm )
293 {
294     return
295         (rStrm.getRecId() == BIFF2_ID_BOF) ||
296         (rStrm.getRecId() == BIFF3_ID_BOF) ||
297         (rStrm.getRecId() == BIFF4_ID_BOF) ||
298         (rStrm.getRecId() == BIFF5_ID_BOF);
299 }
300 
301 /*static*/ bool BiffHelper::skipRecordBlock( BiffInputStream& rStrm, sal_uInt16 nEndRecId )
302 {
303     sal_uInt16 nStartRecId = rStrm.getRecId();
304     while( rStrm.startNextRecord() && (rStrm.getRecId() != nEndRecId) )
305         if( rStrm.getRecId() == nStartRecId )
306             skipRecordBlock( rStrm, nEndRecId );
307     return !rStrm.isEof() && (rStrm.getRecId() == nEndRecId);
308 }
309 
310 /*static*/ void BiffHelper::importImgData( StreamDataSequence& orDataSeq, BiffInputStream& rStrm, BiffType eBiff )
311 {
312     sal_uInt16 nFormat, nEnv;
313     sal_Int32 nBytes;
314     rStrm >> nFormat >> nEnv >> nBytes;
315     OSL_ENSURE( nBytes > 0, "BiffHelper::importImgData - invalid data size" );
316     if( (0 < nBytes) && (nBytes <= rStrm.getRemaining()) )
317     {
318         switch( nFormat )
319         {
320 //            case BIFF_IMGDATA_WMF:      /* TODO */                                              break;
321             case BIFF_IMGDATA_DIB:      lclImportImgDataDib( orDataSeq, rStrm, nBytes, eBiff ); break;
322 //            case BIFF_IMGDATA_NATIVE:   /* TODO */                                              break;
323             default:                    OSL_ENSURE( false, "BiffHelper::importImgData - unknown image format" );
324         }
325     }
326 }
327 
328 // ============================================================================
329 
330 } // namespace xls
331 } // namespace oox
332