xref: /core/basic/source/classes/image.cxx (revision 49c88d8b)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <tools/stream.hxx>
21 #include <tools/tenccvt.hxx>
22 #include <osl/thread.h>
23 #include <sal/log.hxx>
24 #include <basic/sbx.hxx>
25 #include <sb.hxx>
26 #include <string.h>
27 #include <image.hxx>
28 #include <codegen.hxx>
29 #include <memory>
30 
31 SbiImage::SbiImage()
32 {
33     pStrings   = nullptr;
34     pCode      = nullptr;
35     pLegacyPCode = nullptr;
36     nFlags     = SbiImageFlags::NONE;
37     nStringSize= 0;
38     nCodeSize  = 0;
39     nLegacyCodeSize  =
40     nDimBase   = 0;
41     bInit      =
42     bError     = false;
43     bFirstInit = true;
44     eCharSet   = osl_getThreadTextEncoding();
45     nStringIdx = 0;
46     nStringOff = 0;
47 }
48 
49 SbiImage::~SbiImage()
50 {
51     Clear();
52 }
53 
54 void SbiImage::Clear()
55 {
56     mvStringOffsets.clear();
57     pStrings.reset();
58     pCode.reset();
59     pLegacyPCode.reset();
60     pStrings   = nullptr;
61     pCode      = nullptr;
62     nFlags     = SbiImageFlags::NONE;
63     nStringSize= 0;
64     nLegacyCodeSize  = 0;
65     nCodeSize  = 0;
66     eCharSet   = osl_getThreadTextEncoding();
67     nDimBase   = 0;
68     bError     = false;
69 }
70 
71 static bool SbiGood( SvStream const & r )
72 {
73     return r.good();
74 }
75 
76 // Open Record
77 static sal_uInt64 SbiOpenRecord( SvStream& r, FileOffset nSignature, sal_uInt16 nElem )
78 {
79     sal_uInt64 nPos = r.Tell();
80     r.WriteUInt16( static_cast<sal_uInt16>( nSignature ) )
81         .WriteInt32( 0 ).WriteUInt16( nElem );
82     return nPos;
83 }
84 
85 // Close Record
86 static void SbiCloseRecord( SvStream& r, sal_uInt64 nOff )
87 {
88     sal_uInt64 nPos = r.Tell();
89     r.Seek( nOff + 2 );
90     r.WriteInt32(nPos - nOff - 8 );
91     r.Seek( nPos );
92 }
93 
94 bool SbiImage::Load( SvStream& r, sal_uInt32& nVersion )
95 {
96 
97     sal_uInt16 nSign, nCount;
98     sal_uInt32 nLen, nOff;
99 
100     Clear();
101     // Read Master-Record
102     r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount );
103     sal_uInt64 nLast = r.Tell() + nLen;
104     sal_uInt32 nCharSet;               // System charset
105     sal_uInt32 lDimBase;
106     sal_uInt16 nReserved1;
107     sal_uInt32 nReserved2;
108     sal_uInt32 nReserved3;
109     bool bBadVer = false;
110     if( nSign == static_cast<sal_uInt16>( FileOffset::Module ) )
111     {
112         sal_uInt16 nTmpFlags;
113         r.ReadUInt32( nVersion ).ReadUInt32( nCharSet ).ReadUInt32( lDimBase )
114          .ReadUInt16( nTmpFlags ).ReadUInt16( nReserved1 ).ReadUInt32( nReserved2 ).ReadUInt32( nReserved3 );
115         nFlags = static_cast<SbiImageFlags>(nTmpFlags);
116         eCharSet = nCharSet;
117         eCharSet = GetSOLoadTextEncoding( eCharSet );
118         bBadVer  = ( nVersion > B_CURVERSION );
119         nDimBase = static_cast<sal_uInt16>(lDimBase);
120     }
121 
122     bool bLegacy = ( nVersion < B_EXT_IMG_VERSION );
123 
124     sal_uInt64 nNext;
125     while( ( nNext = r.Tell() ) < nLast )
126     {
127 
128         r.ReadUInt16( nSign ).ReadUInt32( nLen ).ReadUInt16( nCount );
129         nNext += nLen + 8;
130         if( r.GetError() == ERRCODE_NONE )
131         {
132             switch( static_cast<FileOffset>( nSign ) )
133             {
134             case FileOffset::Name:
135                 aName = r.ReadUniOrByteString(eCharSet);
136                 break;
137             case FileOffset::Comment:
138                 aComment = r.ReadUniOrByteString(eCharSet );
139                 break;
140             case FileOffset::Source:
141             {
142                 aOUSource = r.ReadUniOrByteString(eCharSet);
143                 break;
144             }
145             case FileOffset::ExtSource:
146             {
147                 //assuming an empty string with just the lead 32bit/16bit len indicator
148                 const size_t nMinStringSize = (eCharSet == RTL_TEXTENCODING_UNICODE) ? 4 : 2;
149                 const sal_uInt64 nMaxStrings = r.remainingSize() / nMinStringSize;
150                 if (nCount > nMaxStrings)
151                 {
152                     SAL_WARN("basic", "Parsing error: " << nMaxStrings <<
153                              " max possible entries, but " << nCount << " claimed, truncating");
154                     nCount = nMaxStrings;
155                 }
156                 for( sal_uInt16 j = 0; j < nCount; ++j)
157                 {
158                     aOUSource += r.ReadUniOrByteString(eCharSet);
159                 }
160                 break;
161             }
162             case FileOffset::PCode:
163                 if( bBadVer ) break;
164                 pCode.reset(new char[ nLen ]);
165                 nCodeSize = nLen;
166                 r.ReadBytes(pCode.get(), nCodeSize);
167                 if ( bLegacy )
168                 {
169                     nLegacyCodeSize = static_cast<sal_uInt16>(nCodeSize);
170                     pLegacyPCode = std::move(pCode);
171 
172                     PCodeBuffConvertor< sal_uInt16, sal_uInt32 > aLegacyToNew( reinterpret_cast<sal_uInt8*>(pLegacyPCode.get()), nLegacyCodeSize );
173                     aLegacyToNew.convert();
174                     pCode.reset(reinterpret_cast<char*>(aLegacyToNew.GetBuffer()));
175                     nCodeSize = aLegacyToNew.GetSize();
176                     // we don't release the legacy buffer
177                     // right now, that's because the module
178                     // needs it to fix up the method
179                     // nStart members. When that is done
180                     // the module can release the buffer
181                     // or it can wait until this routine
182                     // is called again or when this class                       // destructs all of which will trigger
183                     // release of the buffer.
184                 }
185                 break;
186             case FileOffset::Publics:
187             case FileOffset::PoolDir:
188             case FileOffset::SymPool:
189             case FileOffset::LineRanges:
190                 break;
191             case FileOffset::StringPool:
192             {
193                 if( bBadVer ) break;
194                 //assuming an empty string with just the lead 32bit len indicator
195                 const sal_uInt64 nMinStringSize = 4;
196                 const sal_uInt64 nMaxStrings = r.remainingSize() / nMinStringSize;
197                 if (nCount > nMaxStrings)
198                 {
199                     SAL_WARN("basic", "Parsing error: " << nMaxStrings <<
200                              " max possible entries, but " << nCount << " claimed, truncating");
201                     nCount = nMaxStrings;
202                 }
203                 MakeStrings( nCount );
204                 for( size_t i = 0; i < mvStringOffsets.size() && SbiGood( r ); i++ )
205                 {
206                     r.ReadUInt32( nOff );
207                     mvStringOffsets[ i ] = static_cast<sal_uInt16>(nOff);
208                 }
209                 r.ReadUInt32( nLen );
210                 if( SbiGood( r ) )
211                 {
212                     pStrings.reset(new sal_Unicode[ nLen ]);
213                     nStringSize = static_cast<sal_uInt16>(nLen);
214 
215                     std::unique_ptr<char[]> pByteStrings(new char[ nLen ]);
216                     r.ReadBytes(pByteStrings.get(), nStringSize);
217                     for( size_t j = 0; j < mvStringOffsets.size(); j++ )
218                     {
219                         sal_uInt16 nOff2 = static_cast<sal_uInt16>(mvStringOffsets[ j ]);
220                         OUString aStr( pByteStrings.get() + nOff2, strlen(pByteStrings.get() + nOff2), eCharSet );
221                         memcpy( pStrings.get() + nOff2, aStr.getStr(), (aStr.getLength() + 1) * sizeof( sal_Unicode ) );
222                     }
223                 }
224                 break;
225             }
226             case FileOffset::UserTypes:
227             {
228                 //assuming an empty string with just the lead 32bit/16bit len indicator
229                 const size_t nMinStringSize = (eCharSet == RTL_TEXTENCODING_UNICODE) ? 4 : 2;
230                 const sal_uInt64 nMinRecordSize = nMinStringSize + sizeof(sal_Int16);
231                 const sal_uInt64 nMaxRecords = r.remainingSize() / nMinRecordSize;
232                 if (nCount > nMaxRecords)
233                 {
234                     SAL_WARN("basic", "Parsing error: " << nMaxRecords <<
235                              " max possible entries, but " << nCount << " claimed, truncating");
236                     nCount = nMaxRecords;
237                 }
238 
239                 // User defined types
240                 for (sal_uInt16 i = 0; i < nCount; i++)
241                 {
242                     OUString aTypeName = r.ReadUniOrByteString(eCharSet);
243 
244                     sal_uInt16 nTypeMembers;
245                     r.ReadUInt16(nTypeMembers);
246 
247                     const sal_uInt64 nMaxTypeMembers = r.remainingSize() / 8;
248                     if (nTypeMembers > nMaxTypeMembers)
249                     {
250                         SAL_WARN("basic", "Parsing error: " << nMaxTypeMembers <<
251                                  " max possible entries, but " << nTypeMembers << " claimed, truncating");
252                         nTypeMembers = nMaxTypeMembers;
253                     }
254 
255                     SbxObject *pType = new SbxObject(aTypeName);
256                     SbxArray *pTypeMembers = pType->GetProperties();
257 
258                     for (sal_uInt16 j = 0; j < nTypeMembers; j++)
259                     {
260                         OUString aMemberName = r.ReadUniOrByteString(eCharSet);
261 
262                         sal_Int16 aIntMemberType;
263                         r.ReadInt16(aIntMemberType);
264                         SbxDataType aMemberType = static_cast< SbxDataType > ( aIntMemberType );
265 
266                         SbxProperty *pTypeElem = new SbxProperty( aMemberName, aMemberType );
267 
268                         sal_uInt32 aIntFlag;
269                         r.ReadUInt32(aIntFlag);
270                         SbxFlagBits nElemFlags = static_cast< SbxFlagBits > ( aIntFlag );
271 
272                         pTypeElem->SetFlags(nElemFlags);
273 
274                         sal_Int16 hasObject;
275                         r.ReadInt16(hasObject);
276 
277                         if (hasObject == 1)
278                         {
279                             if(aMemberType == SbxOBJECT)
280                             {
281                                 // nested user defined types
282                                 // declared before use, so it is ok to reference it by name on load
283                                 OUString aNestedTypeName = r.ReadUniOrByteString(eCharSet);
284                                 SbxObject* pNestedTypeObj = static_cast< SbxObject* >( rTypes->Find( aNestedTypeName, SbxClassType::Object ) );
285                                 if (pNestedTypeObj)
286                                 {
287                                     SbxObject* pCloneObj = cloneTypeObjectImpl( *pNestedTypeObj );
288                                     pTypeElem->PutObject( pCloneObj );
289                                 }
290                             }
291                             else
292                             {
293                                 // an array
294                                 SbxDimArray* pArray = new SbxDimArray();
295 
296                                 sal_Int16 isFixedSize;
297                                 r.ReadInt16(isFixedSize);
298                                 if (isFixedSize == 1)
299                                     pArray->setHasFixedSize( true );
300 
301                                 sal_Int32 nDims;
302                                 r.ReadInt32(nDims);
303                                 for (sal_Int32 d = 0; d < nDims; d++)
304                                 {
305                                     sal_Int32 lBound;
306                                     sal_Int32 uBound;
307                                     r.ReadInt32(lBound).ReadInt32(uBound);
308                                     pArray->unoAddDim32(lBound, uBound);
309                                 }
310 
311                                 pTypeElem->PutObject( pArray );
312                             }
313                         }
314 
315                         pTypeMembers->Insert( pTypeElem, pTypeMembers->Count() );
316 
317                     }
318 
319                     pType->Remove( "Name", SbxClassType::DontCare );
320                     pType->Remove( "Parent", SbxClassType::DontCare );
321 
322                     AddType(pType);
323                 }
324                 break;
325             }
326             case FileOffset::ModEnd:
327                 goto done;
328             default:
329                 break;
330             }
331         }
332         else
333         {
334             break;
335         }
336         r.Seek( nNext );
337     }
338 done:
339     r.Seek( nLast );
340     if( !SbiGood( r ) )
341     {
342         bError = true;
343     }
344     return !bError;
345 }
346 
347 bool SbiImage::Save( SvStream& r, sal_uInt32 nVer )
348 {
349     bool bLegacy = ( nVer < B_EXT_IMG_VERSION );
350 
351     // detect if old code exceeds legacy limits
352     // if so, then disallow save
353     if ( bLegacy && ExceedsLegacyLimits() )
354     {
355         SbiImage aEmptyImg;
356         aEmptyImg.aName = aName;
357         aEmptyImg.Save( r, B_LEGACYVERSION );
358         return true;
359     }
360     // First of all the header
361     sal_uInt64 nStart = SbiOpenRecord( r, FileOffset::Module, 1 );
362     sal_uInt64 nPos;
363 
364     eCharSet = GetSOStoreTextEncoding( eCharSet );
365     if ( bLegacy )
366     {
367         r.WriteInt32( B_LEGACYVERSION );
368     }
369     else
370     {
371         r.WriteInt32( B_CURVERSION );
372     }
373     r .WriteInt32( eCharSet )
374       .WriteInt32( nDimBase )
375       .WriteInt16( static_cast<sal_uInt16>(nFlags) )
376       .WriteInt16( 0 )
377       .WriteInt32( 0 )
378       .WriteInt32( 0 );
379 
380     // Name?
381     if( !aName.isEmpty() && SbiGood( r ) )
382     {
383         nPos = SbiOpenRecord( r, FileOffset::Name, 1 );
384         r.WriteUniOrByteString( aName, eCharSet );
385         SbiCloseRecord( r, nPos );
386     }
387     // Comment?
388     if( !aComment.isEmpty() && SbiGood( r ) )
389     {
390         nPos = SbiOpenRecord( r, FileOffset::Comment, 1 );
391         r.WriteUniOrByteString( aComment, eCharSet );
392         SbiCloseRecord( r, nPos );
393     }
394     // Source?
395     if( !aOUSource.isEmpty() && SbiGood( r ) )
396     {
397         nPos = SbiOpenRecord( r, FileOffset::Source, 1 );
398         r.WriteUniOrByteString( aOUSource, eCharSet );
399         SbiCloseRecord( r, nPos );
400     }
401     // Binary data?
402     if( pCode && SbiGood( r ) )
403     {
404         nPos = SbiOpenRecord( r, FileOffset::PCode, 1 );
405         if ( bLegacy )
406         {
407             PCodeBuffConvertor< sal_uInt32, sal_uInt16 > aNewToLegacy( reinterpret_cast<sal_uInt8*>(pCode.get()), nCodeSize );
408             aNewToLegacy.convert();
409             pLegacyPCode.reset(reinterpret_cast<char*>(aNewToLegacy.GetBuffer()));
410             nLegacyCodeSize = aNewToLegacy.GetSize();
411             r.WriteBytes(pLegacyPCode.get(), nLegacyCodeSize);
412         }
413         else
414         {
415             r.WriteBytes(pCode.get(), nCodeSize);
416         }
417         SbiCloseRecord( r, nPos );
418     }
419     // String-Pool?
420     if( !mvStringOffsets.empty() )
421     {
422         nPos = SbiOpenRecord( r, FileOffset::StringPool, mvStringOffsets.size() );
423         // For every String:
424         //  sal_uInt32 Offset of the Strings in the Stringblock
425         for( size_t i = 0; i < mvStringOffsets.size() && SbiGood( r ); i++ )
426         {
427             r.WriteUInt32( mvStringOffsets[ i ] );
428         }
429         // Then the String-Block
430         std::unique_ptr<char[]> pByteStrings(new char[ nStringSize ]);
431         for( size_t i = 0; i < mvStringOffsets.size(); i++ )
432         {
433             sal_uInt16 nOff = static_cast<sal_uInt16>(mvStringOffsets[ i ]);
434             OString aStr(OUStringToOString(OUString(pStrings.get() + nOff), eCharSet));
435             memcpy( pByteStrings.get() + nOff, aStr.getStr(), (aStr.getLength() + 1) * sizeof( char ) );
436         }
437         r.WriteUInt32( nStringSize );
438         r.WriteBytes(pByteStrings.get(), nStringSize);
439 
440         pByteStrings.reset();
441         SbiCloseRecord( r, nPos );
442     }
443     // User defined types
444     if ( rTypes.is() )
445     {
446         sal_uInt16 nTypes = rTypes->Count();
447         if (nTypes > 0 )
448         {
449             nPos = SbiOpenRecord( r, FileOffset::UserTypes, nTypes );
450 
451             for (sal_uInt16 i = 0; i < nTypes; i++)
452             {
453                 SbxObject* pType = static_cast< SbxObject* > ( rTypes->Get(i) );
454                 OUString aTypeName = pType->GetClassName();
455 
456                 r.WriteUniOrByteString( aTypeName, eCharSet );
457 
458                 SbxArray  *pTypeMembers = pType->GetProperties();
459                 sal_uInt16 nTypeMembers = pTypeMembers->Count();
460 
461                 r.WriteInt16(nTypeMembers);
462 
463                 for (sal_uInt16 j = 0; j < nTypeMembers; j++)
464                 {
465 
466                     SbxProperty* pTypeElem = static_cast< SbxProperty* > ( pTypeMembers->Get(j) );
467 
468                     const OUString& aElemName = pTypeElem->GetName();
469                     r.WriteUniOrByteString( aElemName, eCharSet );
470 
471                     SbxDataType dataType =   pTypeElem->GetType();
472                     r.WriteInt16(dataType);
473 
474                     SbxFlagBits nElemFlags = pTypeElem->GetFlags();
475                     r.WriteUInt32(static_cast< sal_uInt32 > (nElemFlags) );
476 
477                     SbxBase* pElemObject = pTypeElem->GetObject();
478 
479                     if (pElemObject)
480                     {
481                         r.WriteInt16(1); // has elem Object
482 
483                         if( dataType == SbxOBJECT )
484                         {
485                             // nested user defined types
486                             // declared before use, so it is ok to reference it by name on load
487                             SbxObject* pNestedType = static_cast< SbxObject* > ( pElemObject );
488                             r.WriteUniOrByteString( pNestedType->GetClassName(), eCharSet );
489                         }
490                         else
491                         {
492                             // an array
493                             SbxDimArray* pArray = static_cast< SbxDimArray* > ( pElemObject );
494 
495                             bool bFixedSize = pArray->hasFixedSize();
496                             if (bFixedSize)
497                                 r.WriteInt16(1);
498                             else
499                                 r.WriteInt16(0);
500 
501                             sal_Int32 nDims = pArray->GetDims();
502                             r.WriteInt32(nDims);
503 
504                             for (sal_Int32 d = 0; d < nDims; d++)
505                             {
506                                 sal_Int32 lBound;
507                                 sal_Int32 uBound;
508                                 pArray->GetDim32(d, lBound, uBound);
509                                 r.WriteInt32(lBound).WriteInt32(uBound);
510                             }
511                         }
512                     }
513                     else
514                         r.WriteInt16(0); // no elem Object
515 
516                 }
517             }
518             SbiCloseRecord( r, nPos );
519         }
520     }
521     // Set overall length
522     SbiCloseRecord( r, nStart );
523     if( !SbiGood( r ) )
524     {
525         bError = true;
526     }
527     return !bError;
528 }
529 
530 void SbiImage::MakeStrings( short nSize )
531 {
532     nStringIdx = 0;
533     nStringOff = 0;
534     nStringSize = 1024;
535     pStrings.reset( new sal_Unicode[ nStringSize ]);
536     mvStringOffsets.resize(nSize);
537     if (nSize != 0) {
538         memset( mvStringOffsets.data(), 0, nSize * sizeof( sal_uInt32 ) );
539     }
540     memset( pStrings.get(), 0, nStringSize * sizeof( sal_Unicode ) );
541 }
542 
543 // Add a string to StringPool. The String buffer is dynamically
544 // growing in 1K-Steps
545 void SbiImage::AddString( const OUString& r )
546 {
547     if( nStringIdx >= short(mvStringOffsets.size()) )
548     {
549         bError = true;
550     }
551     if( !bError )
552     {
553         sal_Int32  len = r.getLength() + 1;
554         sal_uInt32 needed = nStringOff + len;
555         if( needed > 0xFFFFFF00 )
556         {
557             bError = true;  // out of mem!
558         }
559         else if( needed > nStringSize )
560         {
561             sal_uInt32 nNewLen = needed + 1024;
562             nNewLen &= 0xFFFFFC00;  // trim to 1K border
563             std::unique_ptr<sal_Unicode[]> p(new sal_Unicode[nNewLen]);
564             memcpy( p.get(), pStrings.get(), nStringSize * sizeof( sal_Unicode ) );
565             pStrings = std::move(p);
566             nStringSize = sal::static_int_cast< sal_uInt16 >(nNewLen);
567         }
568         if( !bError )
569         {
570             mvStringOffsets[ nStringIdx++ ] = nStringOff;
571             memcpy( pStrings.get() + nStringOff, r.getStr(), len * sizeof( sal_Unicode ) );
572             nStringOff = nStringOff + len;
573             // Last String? The update the size of the buffer
574             if( nStringIdx >= short(mvStringOffsets.size()) )
575             {
576                 nStringSize = nStringOff;
577             }
578         }
579     }
580 }
581 
582 // Add code block
583 // The block was fetched by the compiler from class SbBuffer and
584 // is already created with new. Additionally it contains all Integers
585 // in Big Endian format, so can be directly read/written.
586 void SbiImage::AddCode( std::unique_ptr<char[]> p, sal_uInt32 s )
587 {
588     pCode = std::move(p);
589     nCodeSize = s;
590 }
591 
592 // Add user type
593 void SbiImage::AddType(SbxObject const * pObject)
594 {
595     if( !rTypes.is() )
596     {
597         rTypes = new SbxArray;
598     }
599     SbxObject *pCopyObject = new SbxObject(*pObject);
600     rTypes->Insert (pCopyObject,rTypes->Count());
601 }
602 
603 void SbiImage::AddEnum(SbxObject* pObject) // Register enum type
604 {
605     if( !rEnums.is() )
606     {
607         rEnums = new SbxArray;
608     }
609     rEnums->Insert( pObject, rEnums->Count() );
610 }
611 
612 // Note: IDs start with 1
613 OUString SbiImage::GetString( short nId ) const
614 {
615     if( nId && nId <= short(mvStringOffsets.size()) )
616     {
617         sal_uInt32 nOff = mvStringOffsets[ nId - 1 ];
618         sal_Unicode* pStr = pStrings.get() + nOff;
619 
620         // #i42467: Special treatment for vbNullChar
621         if( *pStr == 0 )
622         {
623             sal_uInt32 nNextOff = (nId < short(mvStringOffsets.size())) ? mvStringOffsets[ nId ] : nStringOff;
624             sal_uInt32 nLen = nNextOff - nOff - 1;
625             if( nLen == 1 )
626             {
627                 // Force length 1 and make char 0 afterwards
628                 OUString aNullCharStr( u'\0');
629                 return aNullCharStr;
630             }
631         }
632         else
633         {
634             return OUString(pStr);
635         }
636     }
637     return OUString();
638 }
639 
640 const SbxObject* SbiImage::FindType (const OUString& aTypeName) const
641 {
642     return rTypes.is() ? static_cast<SbxObject*>(rTypes->Find(aTypeName,SbxClassType::Object)) : nullptr;
643 }
644 
645 sal_uInt16 SbiImage::CalcLegacyOffset( sal_Int32 nOffset )
646 {
647     return SbiCodeGen::calcLegacyOffSet( reinterpret_cast<sal_uInt8*>(pCode.get()), nOffset ) ;
648 }
649 
650 sal_uInt32 SbiImage::CalcNewOffset( sal_Int16 nOffset )
651 {
652     return SbiCodeGen::calcNewOffSet( reinterpret_cast<sal_uInt8*>(pLegacyPCode.get()), nOffset ) ;
653 }
654 
655 void  SbiImage::ReleaseLegacyBuffer()
656 {
657     pLegacyPCode.reset();
658     nLegacyCodeSize = 0;
659 }
660 
661 bool SbiImage::ExceedsLegacyLimits()
662 {
663     return ( nStringSize > 0xFF00 ) || ( CalcLegacyOffset( nCodeSize ) > 0xFF00 );
664 }
665 
666 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
667