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