//************************************************************************************** // Filename: CTextRunsToUnicode.cp // Copyright © 2000 Tomasz Kukielka. All rights reserved. // // Description: #include "CTextRunsToUnicode.h" #include "CConverter.h" CTextRunsToUnicode::CTextRunsToUnicode(TArray *inRunsArray, TextEncodingFormat inUnicodeFormat) : mRunsArray(inRunsArray) { //other formats are not supported by TEC 1.5 Assert_( (inUnicodeFormat == kUnicodeUTF8Format) || (inUnicodeFormat == kUnicode16BitFormat) ); Assert_( mRunsArray != NULL ); mUnicodeEncoding = ::ResolveDefaultTextEncoding(kTextEncodingUnicodeDefault); mUnicodeEncoding = ::CreateTextEncoding( ::GetTextEncodingBase(mUnicodeEncoding), kTextEncodingDefaultVariant, inUnicodeFormat ); mTextToUnicodeInfo = NULL; //create one default info - it will be changed with each run UnicodeMapping theMapping; theMapping.unicodeEncoding = mUnicodeEncoding; theMapping.otherEncoding = kTextEncodingMacRoman; theMapping.mappingVersion = kUnicodeUseLatestMapping; OSStatus status = ::CreateTextToUnicodeInfo( &theMapping, &mTextToUnicodeInfo); FailOSErr_(status); } CTextRunsToUnicode::~CTextRunsToUnicode() { if(mTextToUnicodeInfo != NULL) ::DisposeTextToUnicodeInfo( &mTextToUnicodeInfo ); } void CTextRunsToUnicode::ChangeEncoding(TextEncoding &inFromEncoding) { Assert_(mTextToUnicodeInfo != NULL); UnicodeMapping theMapping; theMapping.unicodeEncoding = mUnicodeEncoding; theMapping.otherEncoding = inFromEncoding; theMapping.mappingVersion = kUnicodeUseLatestMapping; OSStatus status = ::ChangeTextToUnicodeInfo(mTextToUnicodeInfo, &theMapping); FailOSErr_(status); } Handle CTextRunsToUnicode::Convert( Handle srcH ) { Assert_(mRunsArray != NULL); StHandleLocker theLock(srcH); return Convert( *srcH, ::GetHandleSize(srcH) ); } Handle CTextRunsToUnicode::Convert( Ptr inSrc, ByteCount srcLen ) { Handle outH = ::NewHandle(0); StHandleBlock outDel(outH); UInt32 prevLen = 0; TextEncoding prevEnc = kTextEncodingUnknown; TArrayIterator iterator(*mRunsArray); EncodingRun oneRun; while( iterator.Next(oneRun) ) { if(oneRun.enc != prevEnc) { ChangeEncoding(oneRun.enc); } Handle oneH = ConvertOneRun( inSrc + prevLen, oneRun.len );//returns non-nil handle or throws StHandleBlock oneDel(oneH); OSErr iErr = ::HandAndHand( oneH, outH ); FailOSErr_(iErr); prevEnc = oneRun.enc; prevLen += oneRun.len; } //something wrong with runs, but we have to handle it if(srcLen > prevLen) { Handle oneH = ConvertOneRun( inSrc + prevLen, srcLen - prevLen );//returns non-nil handle or throws StHandleBlock oneDel(oneH); OSErr iErr = ::HandAndHand( oneH, outH ); FailOSErr_(iErr); } outDel.Release(); return outH; } Handle CTextRunsToUnicode::ConvertOneRun( Handle srcH ) { StHandleLocker theLock(srcH); return ConvertOneRun( *srcH, ::GetHandleSize(srcH) ); } Handle CTextRunsToUnicode::ConvertOneRun( Ptr inSrc, ByteCount srcLen ) { Assert_(mTextToUnicodeInfo != NULL); OSStatus status = noErr; ByteCount maxOutput = srcLen;//allocate the same as input buffer for a start if( maxOutput < kMinimumBufferSize ) maxOutput = kMinimumBufferSize; Handle outH = ::NewHandle(maxOutput); ThrowIfNil_(outH); //create auto deleter for our handle StHandleBlock outBlock(outH); ::HLock(outH); ByteCount origOutputSize = maxOutput; ByteCount outputLen = 0; char *dest = *outH; do { ByteCount tSourceRead, tOutputLen; status = ::ConvertFromTextToUnicode( mTextToUnicodeInfo, srcLen, (ConstLogicalAddress) inSrc, kUnicodeUseFallbacksMask | kUnicodeKeepInfoMask, 0, nil, nil, nil, maxOutput, &tSourceRead, &tOutputLen, (UniCharArrayPtr)dest ); //check to see if we need to adjust our output buffer size. See discussion of this in ConvertFromMulti method. if( status == kTECOutputBufferFullStatus || status == kTECBufferBelowMinimumSizeErr ) { CConverter::ResizeOutputHandle( outH, &dest, origOutputSize ); maxOutput = origOutputSize + (maxOutput - tOutputLen); } else maxOutput -= tOutputLen; outputLen += tOutputLen; dest = (char*)( (UInt32)dest + tOutputLen); inSrc = (char*)( (UInt32)inSrc + tSourceRead); srcLen -= tSourceRead; } while( status == kTECOutputBufferFullStatus || status == kTECBufferBelowMinimumSizeErr ); ::HUnlock(outH); if( (status == noErr) || (status == kTECUsedFallbacksStatus) ) { Size newHSize = ::GetHandleSize(outH); if( outputLen < newHSize )//clip the trailing trash { ::SetHandleSize(outH, outputLen); } outBlock.Release();//this is the success situation, in other cases the handle will be deleted } else { FailOSErr_(status); } return outH; }