/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #define INCL_GPI #define INCL_DOS #include #include "tools/svwin.h" #include "vcl/svapp.hxx" #include "os2/salgdi.h" #include "os2/saldata.hxx" // for GetMirroredChar #include "sft.hxx" #include "sallayout.hxx" #include "rtl/ustring.hxx" #include "osl/module.h" #include "sallayout.hxx" #ifndef __H_FT2LIB #include #include #endif #include #include #ifdef GCP_KERN_HACK #include #endif // GCP_KERN_HACK #include typedef std::hash_map IntMap; #define DROPPED_OUTGLYPH 0xFFFF using namespace rtl; // ======================================================================= // OS/2 specific physical font instance class ImplOs2FontEntry : public ImplFontEntry { public: ImplOs2FontEntry( ImplFontSelectData& ); ~ImplOs2FontEntry(); private: // TODO: also add HFONT??? Watch out for issues with too many active fonts... #ifdef GCP_KERN_HACK public: bool HasKernData() const; void SetKernData( int, const KERNINGPAIRS* ); int GetKerning( sal_Unicode, sal_Unicode ) const; private: KERNINGPAIRS* mpKerningPairs; int mnKerningPairs; #endif // GCP_KERN_HACK public: int GetCachedGlyphWidth( int nCharCode ) const; void CacheGlyphWidth( int nCharCode, int nCharWidth ); private: IntMap maWidthMap; }; // ----------------------------------------------------------------------- inline void ImplOs2FontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth ) { maWidthMap[ nCharCode ] = nCharWidth; } inline int ImplOs2FontEntry::GetCachedGlyphWidth( int nCharCode ) const { IntMap::const_iterator it = maWidthMap.find( nCharCode ); if( it == maWidthMap.end() ) return -1; return it->second; } // ======================================================================= class Os2Layout : public SalLayout { public: Os2Layout( HDC, const ImplOs2FontData&, ImplOs2FontEntry& ); virtual void InitFont() const; void SetFontScale( float f ) { mfFontScale = f; } float GetFontScale() const { return mfFontScale; } protected: HPS mhPS; // OS2 device handle FATTRS mhFont; int mnBaseAdv; // x-offset relative to Layout origin float mfFontScale; // allows metrics emulation of huge font sizes const ImplOs2FontData& mrOs2FontData; ImplOs2FontEntry& mrOs2FontEntry; }; // ======================================================================= class Os2SalLayout : public Os2Layout { public: Os2SalLayout( HPS, PM_BYTE nCharSet, const ImplOs2FontData&, ImplOs2FontEntry& ); virtual ~Os2SalLayout(); virtual bool LayoutText( ImplLayoutArgs& ); virtual void AdjustLayout( ImplLayoutArgs& ); virtual void DrawText( SalGraphics& ) const; virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&, sal_Int32* pGlyphAdvances, int* pCharIndexes ) const; virtual long FillDXArray( long* pDXArray ) const; virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; // for glyph+font+script fallback virtual void MoveGlyph( int nStart, long nNewXPos ); virtual void DropGlyph( int nStart ); virtual void Simplify( bool bIsBase ); protected: void Justify( long nNewWidth ); void ApplyDXArray( const ImplLayoutArgs& ); protected: private: int mnGlyphCount; int mnCharCount; sal_Unicode* mpOutGlyphs; int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[] int* mpGlyphOrigAdvs; int* mpCharWidths; // map rel char pos to char width int* mpChars2Glyphs; // map rel char pos to abs glyph pos int* mpGlyphs2Chars; // map abs glyph pos to abs char pos bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL mutable long mnWidth; bool mbDisableGlyphs; int mnNotdefWidth; PM_BYTE mnCharSet; }; // ======================================================================= Os2Layout::Os2Layout( HPS hPS, const ImplOs2FontData& rWFD, ImplOs2FontEntry& rWFE ) : mhPS( hPS ), mnBaseAdv( 0 ), mfFontScale( 1.0 ), mrOs2FontData( rWFD ), mrOs2FontEntry( rWFE ) { sal_Bool fSuccess; fSuccess = Ft2QueryLogicalFont( mhPS, LCID_BASE, NULL, &mhFont, sizeof(FATTRS)); } // ----------------------------------------------------------------------- void Os2Layout::InitFont() const { // select fallback level 0 font APIRET rc = Ft2CreateLogFont( mhPS, NULL, LCID_BASE, (PFATTRS)&mhFont); } // ======================================================================= Os2SalLayout::Os2SalLayout( HPS hPS, PM_BYTE nCharSet, const ImplOs2FontData& rOs2FontData, ImplOs2FontEntry& rOs2FontEntry ) : Os2Layout( hPS, rOs2FontData, rOs2FontEntry ), mnGlyphCount( 0 ), mnCharCount( 0 ), mpOutGlyphs( NULL ), mpGlyphAdvances( NULL ), mpGlyphOrigAdvs( NULL ), mpCharWidths( NULL ), mpChars2Glyphs( NULL ), mpGlyphs2Chars( NULL ), mpGlyphRTLFlags( NULL ), mnWidth( 0 ), mnNotdefWidth( -1 ), mnCharSet( nCharSet ), mbDisableGlyphs( false ) { mbDisableGlyphs = true; } // ----------------------------------------------------------------------- Os2SalLayout::~Os2SalLayout() { delete[] mpGlyphRTLFlags; delete[] mpGlyphs2Chars; delete[] mpChars2Glyphs; if( mpCharWidths != mpGlyphAdvances ) delete[] mpCharWidths; delete[] mpGlyphOrigAdvs; delete[] mpGlyphAdvances; delete[] mpOutGlyphs; } // ----------------------------------------------------------------------- bool Os2SalLayout::LayoutText( ImplLayoutArgs& rArgs ) { // prepare layout // TODO: fix case when recyclying old Os2SalLayout object mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0); mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos; if( !mbDisableGlyphs ) { // Win32 glyph APIs have serious problems with vertical layout // => workaround is to use the unicode methods then if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL ) mbDisableGlyphs = true; else // use cached value from font face mbDisableGlyphs = mrOs2FontData.IsGlyphApiDisabled(); } // TODO: use a cached value for bDisableAsianKern from upper layers #if 0 if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN ) { TEXTMETRICA aTextMetricA; if( ::GetTextMetricsA( mhDC, &aTextMetricA ) && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) ) rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN; } #endif // layout text int i, j; mnGlyphCount = 0; bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0; // count the number of chars to process if no RTL run rArgs.ResetPos(); bool bHasRTL = false; while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL ) mnGlyphCount += j - i; // if there are RTL runs we need room to remember individual BiDi flags if( bHasRTL ) { mpGlyphRTLFlags = new bool[ mnCharCount ]; for( i = 0; i < mnCharCount; ++i ) mpGlyphRTLFlags[i] = false; } // rewrite the logical string if needed to prepare for the API calls const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos; if( (mnGlyphCount != mnCharCount) || bVertical ) { // we need to rewrite the pBidiStr when any of // - BiDirectional layout // - vertical layout // - partial runs (e.g. with control chars or for glyph fallback) // are involved sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) ); pBidiStr = pRewrittenStr; // note: glyph to char mapping is relative to first character mpChars2Glyphs = new int[ mnCharCount ]; mpGlyphs2Chars = new int[ mnCharCount ]; for( i = 0; i < mnCharCount; ++i ) mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1; mnGlyphCount = 0; rArgs.ResetPos(); bool bIsRTL = false; while( rArgs.GetNextRun( &i, &j, &bIsRTL ) ) { do { // get the next leftmost character in this run int nCharPos = bIsRTL ? --j : i++; sal_Unicode cChar = rArgs.mpStr[ nCharPos ]; // in the RTL case mirror the character and remember its RTL status if( bIsRTL ) { cChar = ::GetMirroredChar( cChar ); mpGlyphRTLFlags[ mnGlyphCount ] = true; } // for vertical writing use vertical alternatives if( bVertical ) { sal_Unicode cVert = ::GetVerticalChar( cChar ); if( cVert ) cChar = cVert; } // rewrite the original string // update the mappings between original and rewritten string pRewrittenStr[ mnGlyphCount ] = cChar; mpGlyphs2Chars[ mnGlyphCount ] = nCharPos; mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount; ++mnGlyphCount; } while( i < j ); } } mpOutGlyphs = new sal_Unicode[ mnGlyphCount ]; mpGlyphAdvances = new int[ mnGlyphCount ]; if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) ) mpGlyphOrigAdvs = new int[ mnGlyphCount ]; #ifndef GCP_KERN_HACK DWORD nGcpOption = 0; // enable kerning if requested if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS ) nGcpOption |= GCP_USEKERNING; #endif // GCP_KERN_HACK LONG lLcid = Ft2QueryCharSet( mhPS); for( i = 0; i < mnGlyphCount; ++i ) mpOutGlyphs[i] = pBidiStr[ i ]; mnWidth = 0; for( i = 0; i < mnGlyphCount; ++i ) { const sal_Unicode* pCodes = &pBidiStr[i]; // check for surrogate pairs if( (pCodes[0] & 0xFC00) == 0xDC00 ) continue; bool bSurrogate = ((pCodes[0] & 0xFC00) == 0xD800); // get the width of the corresponding code point int nCharCode = pCodes[0]; if( bSurrogate ) nCharCode = 0x10000 + ((pCodes[0] & 0x03FF) << 10) + (pCodes[1] & 0x03FF); int nGlyphWidth = mrOs2FontEntry.GetCachedGlyphWidth( nCharCode ); if( nGlyphWidth == -1 ) { if (!Ft2QueryStringWidthW( mhPS, (LPWSTR)&pCodes[0], 1, (LONG*)&nGlyphWidth)) nGlyphWidth = 0; mrOs2FontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth ); } mpGlyphAdvances[ i ] = nGlyphWidth; mnWidth += nGlyphWidth; // remaining codes of surrogate pair get a zero width if( bSurrogate ) mpGlyphAdvances[ i+1 ] = 0; // check with the font face if glyph fallback is needed if( mrOs2FontData.HasChar( nCharCode ) ) continue; // Type1 charmaps are not complete (or buggy), use FT2 to check again if (Ft2FontSupportsUnicodeChar( mhPS, lLcid, TRUE, nCharCode)) continue; #if OSL_DEBUG_LEVEL>0 debug_printf("Os2SalLayout::LayoutText font does not support unicode char\n"); #endif // request glyph fallback at this position in the string bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false; int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos; rArgs.NeedFallback( nCharPos, bRTL ); if( bSurrogate ) rArgs.NeedFallback( nCharPos+1, bRTL ); if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK ) { // when we already are layouting for glyph fallback // then a new unresolved glyph is not interesting mnNotdefWidth = 0; mpOutGlyphs[i] = DROPPED_OUTGLYPH; if( mbDisableGlyphs && bSurrogate ) mpOutGlyphs[i+1] = DROPPED_OUTGLYPH; } else { if( mnNotdefWidth < 0 ) { // get the width of the NotDef glyph LONG aExtent; mnNotdefWidth = 0; if (Ft2QueryStringWidthW( mhPS, (LPWSTR)&rArgs.mpStr[ nCharPos ], 1, &aExtent)) mnNotdefWidth = aExtent; } // use a better NotDef glyph if( !mbDisableGlyphs ) mpOutGlyphs[i] = 0; } // replace the current glyph with the NotDef glyph mnWidth += mnNotdefWidth - mpGlyphAdvances[i]; mpGlyphAdvances[i] = mnNotdefWidth; if( mpGlyphOrigAdvs ) mpGlyphOrigAdvs[i] = mnNotdefWidth; } #ifdef GCP_KERN_HACK // apply kerning if the layout engine has not yet done it if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) ) { #else // GCP_KERN_HACK // apply just asian kerning if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN ) { if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) ) #endif // GCP_KERN_HACK for( i = 0; i < mnGlyphCount; ++i ) mpGlyphOrigAdvs[i] = mpGlyphAdvances[i]; // #99658# also apply asian kerning on the substring border int nLen = mnGlyphCount; if( rArgs.mnMinCharPos + nLen < rArgs.mnLength ) ++nLen; for( i = 1; i < nLen; ++i ) { #ifdef GCP_KERN_HACK if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS ) { int nKernAmount = mrOs2FontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] ); mpGlyphAdvances[ i-1 ] += nKernAmount; mnWidth += nKernAmount; } else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN ) #endif // GCP_KERN_HACK if( (0x3000 == (0xFF00 & pBidiStr[i-1])) && (0x3000 == (0xFF00 & pBidiStr[i])) ) { long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical ); long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical ); long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext; if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 ) { nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4; mpGlyphAdvances[i-1] += nDelta; mnWidth += nDelta; } } } } // calculate virtual char widths if( !mpGlyphs2Chars ) mpCharWidths = mpGlyphAdvances; else { mpCharWidths = new int[ mnCharCount ]; for( i = 0; i < mnCharCount; ++i ) mpCharWidths[ i ] = 0; for( i = 0; i < mnGlyphCount; ++i ) { int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos; if( j >= 0 ) mpCharWidths[ j ] += mpGlyphAdvances[ i ]; } } // scale layout metrics if needed if( mfFontScale != 1.0 ) { mnWidth *= mfFontScale; mnBaseAdv *= mfFontScale; for( i = 0; i < mnCharCount; ++i ) mpCharWidths[ i ] *= mfFontScale; if( mpGlyphAdvances != mpCharWidths ) for( i = 0; i < mnGlyphCount; ++i ) mpGlyphAdvances[ i ] *= mfFontScale; if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) ) for( i = 0; i < mnGlyphCount; ++i ) mpGlyphOrigAdvs[ i ] *= mfFontScale; } return true; } // ----------------------------------------------------------------------- int Os2SalLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIds, Point& rPos, int& nStart, sal_Int32* pGlyphAdvances, int* pCharIndexes ) const { // return zero if no more glyph found if( nStart >= mnGlyphCount ) return 0; // calculate glyph position relative to layout base // TODO: avoid for nStart!=0 case by reusing rPos long nXOffset = mnBaseAdv; for( int i = 0; i < nStart; ++i ) nXOffset += mpGlyphAdvances[ i ]; // calculate absolute position in pixel units Point aRelativePos( nXOffset, 0 ); rPos = GetDrawPosition( aRelativePos ); int nCount = 0; while( nCount < nLen ) { // update return values {aGlyphId,nCharPos,nGlyphAdvance} sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ]; if( mbDisableGlyphs ) { if( mnLayoutFlags & SAL_LAYOUT_VERTICAL ) { sal_Unicode cChar = (sal_Unicode)(aGlyphId & GF_IDXMASK); #ifdef GNG_VERT_HACK if( mrOs2FontData.HasGSUBstitutions( mhPS ) && mrOs2FontData.IsGSUBstituted( cChar ) ) aGlyphId |= GF_ROTL | GF_GSUB; else #endif // GNG_VERT_HACK { aGlyphId |= GetVerticalFlags( cChar ); if( !(aGlyphId & GF_ROTMASK) ) aGlyphId |= GF_VERT; } } aGlyphId |= GF_ISCHAR; } ++nCount; *(pGlyphIds++) = aGlyphId; if( pGlyphAdvances ) *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ]; if( pCharIndexes ) { int nCharPos; if( !mpGlyphs2Chars ) nCharPos = nStart + mnMinCharPos; else nCharPos = mpGlyphs2Chars[nStart]; *(pCharIndexes++) = nCharPos; } // stop at last glyph if( ++nStart >= mnGlyphCount ) break; // stop when next x-position is unexpected if( !pGlyphAdvances && mpGlyphOrigAdvs ) if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] ) break; } return nCount; } // ----------------------------------------------------------------------- void Os2SalLayout::DrawText( SalGraphics& rGraphics ) const { if( mnGlyphCount <= 0 ) return; Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) ); POINTL aPt; APIRET rc; aPt.x = aPos.X(); aPt.y = static_cast(rGraphics).mnHeight - aPos.Y(); // ft2lib doesn't work with printer hps, so we fallback to codepage printing // until cp1200 support will work. if (static_cast(rGraphics).mbPrinter) { // convert to codepage ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() ); // gliph size is not recalculated, so it could be wrong! rc = Ft2CharStringPosAtA( static_cast(rGraphics).mhPS, &aPt, NULL, CHS_VECTOR, mnGlyphCount, (PSZ)str.GetBuffer(), (LONG*)mpGlyphAdvances, 0); } else { // try unicode rendering to screen rc = Ft2CharStringPosAtW( static_cast(rGraphics).mhPS, &aPt, NULL, CHS_VECTOR, mnGlyphCount, (LPWSTR)mpOutGlyphs, (LONG*)mpGlyphAdvances, 0); if (rc == GPI_ERROR) { // if *W fails, convert to codepage and use *A (fallback to GPI into ft2) ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() ); #if OSL_DEBUG_LEVEL>10 debug_printf("Os2SalLayout::DrawText HPS %08x PosAtW failed '%s'!\n",static_cast(rGraphics).mhPS,str.GetBuffer()); #endif // gliph size is not recalculated, so it could be wrong! rc = Ft2CharStringPosAtA( static_cast(rGraphics).mhPS, &aPt, NULL, CHS_VECTOR, mnGlyphCount, (PSZ)str.GetBuffer(), (LONG*)mpGlyphAdvances, 0); } } } // ----------------------------------------------------------------------- long Os2SalLayout::FillDXArray( long* pDXArray ) const { if( !mnWidth ) { long mnWidth = mnBaseAdv; for( int i = 0; i < mnGlyphCount; ++i ) mnWidth += mpGlyphAdvances[ i ]; } if( pDXArray != NULL ) { for( int i = 0; i < mnCharCount; ++i ) pDXArray[ i ] = mpCharWidths[ i ]; } return mnWidth; } // ----------------------------------------------------------------------- int Os2SalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const // NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values { if( mnWidth ) if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth ) return STRING_LEN; long nExtraWidth = mnBaseAdv * nFactor; for( int n = 0; n < mnCharCount; ++n ) { // skip unused characters if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) ) continue; // add char widths until max nExtraWidth += mpCharWidths[ n ] * nFactor; if( nExtraWidth >= nMaxWidth ) return (mnMinCharPos + n); nExtraWidth += nCharExtra; } return STRING_LEN; } // ----------------------------------------------------------------------- void Os2SalLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const { long nXPos = mnBaseAdv; if( !mpGlyphs2Chars ) { for( int i = 0; i < nMaxIdx; i += 2 ) { pCaretXArray[ i ] = nXPos; nXPos += mpGlyphAdvances[ i>>1 ]; pCaretXArray[ i+1 ] = nXPos; } } else { int i; for( i = 0; i < nMaxIdx; ++i ) pCaretXArray[ i ] = -1; // assign glyph positions to character positions for( i = 0; i < mnGlyphCount; ++i ) { int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos; long nXRight = nXPos + mpCharWidths[ nCurrIdx ]; nCurrIdx *= 2; if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) ) { // normal positions for LTR case pCaretXArray[ nCurrIdx ] = nXPos; pCaretXArray[ nCurrIdx+1 ] = nXRight; } else { // reverse positions for RTL case pCaretXArray[ nCurrIdx ] = nXRight; pCaretXArray[ nCurrIdx+1 ] = nXPos; } nXPos += mpGlyphAdvances[ i ]; } } } // ----------------------------------------------------------------------- void Os2SalLayout::Justify( long nNewWidth ) { long nOldWidth = mnWidth; mnWidth = nNewWidth; if( mnGlyphCount <= 0 ) return; if( nNewWidth == nOldWidth ) return; // the rightmost glyph cannot be stretched const int nRight = mnGlyphCount - 1; nOldWidth -= mpGlyphAdvances[ nRight ]; nNewWidth -= mpGlyphAdvances[ nRight ]; // count stretchable glyphs int nStretchable = 0, i; for( i = 0; i < nRight; ++i ) if( mpGlyphAdvances[i] >= 0 ) ++nStretchable; // stretch these glyphs int nDiffWidth = nNewWidth - nOldWidth; for( i = 0; (i < nRight) && (nStretchable > 0); ++i ) { if( mpGlyphAdvances[i] <= 0 ) continue; int nDeltaWidth = nDiffWidth / nStretchable; mpGlyphAdvances[i] += nDeltaWidth; --nStretchable; nDiffWidth -= nDeltaWidth; } } // ----------------------------------------------------------------------- void Os2SalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) { SalLayout::AdjustLayout( rArgs ); // adjust positions if requested if( rArgs.mpDXArray ) ApplyDXArray( rArgs ); else if( rArgs.mnLayoutWidth ) Justify( rArgs.mnLayoutWidth ); else return; // recalculate virtual char widths if they were changed if( mpCharWidths != mpGlyphAdvances ) { int i; if( !mpGlyphs2Chars ) { // standard LTR case for( i = 0; i < mnGlyphCount; ++i ) mpCharWidths[ i ] = mpGlyphAdvances[ i ]; } else { // BiDi or complex case for( i = 0; i < mnCharCount; ++i ) mpCharWidths[ i ] = 0; for( i = 0; i < mnGlyphCount; ++i ) { int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos; if( j >= 0 ) mpCharWidths[ j ] += mpGlyphAdvances[ i ]; } } } } // ----------------------------------------------------------------------- void Os2SalLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) { // try to avoid disturbance of text flow for LSB rounding case; const long* pDXArray = rArgs.mpDXArray; int i = 0; long nOldWidth = mnBaseAdv; for(; i < mnCharCount; ++i ) { int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i]; if( j >= 0 ) { nOldWidth += mpGlyphAdvances[ j ]; int nDiff = nOldWidth - pDXArray[ i ]; // disabled because of #104768# // works great for static text, but problems when typing // if( nDiff>+1 || nDiff<-1 ) // only bother with changing anything when something moved if( nDiff != 0 ) break; } } if( i >= mnCharCount ) return; if( !mpGlyphOrigAdvs ) { mpGlyphOrigAdvs = new int[ mnGlyphCount ]; for( i = 0; i < mnGlyphCount; ++i ) mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ]; } mnWidth = mnBaseAdv; for( i = 0; i < mnCharCount; ++i ) { int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i]; if( j >= 0 ) mpGlyphAdvances[j] = pDXArray[i] - mnWidth; mnWidth = pDXArray[i]; } } // ----------------------------------------------------------------------- void Os2SalLayout::MoveGlyph( int nStart, long nNewXPos ) { if( nStart > mnGlyphCount ) return; // calculate the current x-position of the requested glyph // TODO: cache absolute positions int nXPos = mnBaseAdv; for( int i = 0; i < nStart; ++i ) nXPos += mpGlyphAdvances[i]; // calculate the difference to the current glyph position int nDelta = nNewXPos - nXPos; // adjust the width of the layout if it was already cached if( mnWidth ) mnWidth += nDelta; // depending on whether the requested glyph is leftmost in the layout // adjust either the layout's or the requested glyph's relative position if( nStart > 0 ) mpGlyphAdvances[ nStart-1 ] += nDelta; else mnBaseAdv += nDelta; } // ----------------------------------------------------------------------- void Os2SalLayout::DropGlyph( int nStart ) { mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH; } // ----------------------------------------------------------------------- void Os2SalLayout::Simplify( bool bIsBase ) { // return early if no glyph has been dropped int i = mnGlyphCount; while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) ); if( i < 0 ) return; // convert the layout to a sparse layout if it is not already if( !mpGlyphs2Chars ) { mpGlyphs2Chars = new int[ mnGlyphCount ]; mpCharWidths = new int[ mnCharCount ]; // assertion: mnGlyphCount == mnCharCount for( int k = 0; k < mnGlyphCount; ++k ) { mpGlyphs2Chars[ k ] = mnMinCharPos + k; mpCharWidths[ k ] = mpGlyphAdvances[ k ]; } } // remove dropped glyphs that are rightmost in the layout for( i = mnGlyphCount; --i >= 0; ) { if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH ) break; if( mnWidth ) mnWidth -= mpGlyphAdvances[ i ]; int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos; if( nRelCharPos >= 0 ) mpCharWidths[ nRelCharPos ] = 0; } mnGlyphCount = i + 1; // keep original glyph widths around if( !mpGlyphOrigAdvs ) { mpGlyphOrigAdvs = new int[ mnGlyphCount ]; for( int k = 0; k < mnGlyphCount; ++k ) mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ]; } // remove dropped glyphs inside the layout int nNewGC = 0; for( i = 0; i < mnGlyphCount; ++i ) { if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH ) { // adjust relative position to last valid glyph int nDroppedWidth = mpGlyphAdvances[ i ]; mpGlyphAdvances[ i ] = 0; if( nNewGC > 0 ) mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth; else mnBaseAdv += nDroppedWidth; // zero the virtual char width for the char that has a fallback int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos; if( nRelCharPos >= 0 ) mpCharWidths[ nRelCharPos ] = 0; } else { if( nNewGC != i ) { // rearrange the glyph array to get rid of the dropped glyph mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ]; mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ]; mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ]; mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ]; } ++nNewGC; } } mnGlyphCount = nNewGC; if( mnGlyphCount <= 0 ) mnWidth = mnBaseAdv = 0; } // ======================================================================= SalLayout* Os2SalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) { Os2SalLayout* pLayout = NULL; DBG_ASSERT( mpOs2FontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL"); const ImplOs2FontData& rFontFace = *mpOs2FontData[ nFallbackLevel ]; ImplOs2FontEntry& rFontInstance = *mpOs2FontEntry[ nFallbackLevel ]; { #ifdef GCP_KERN_HACK if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() ) { // TODO: directly cache kerning info in the rFontInstance // TODO: get rid of kerning methods+data in WinSalGraphics object GetKernPairs( 0, NULL ); rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs ); } #endif // GCP_KERN_HACK //PM_BYTE eCharSet = ANSI_CHARSET; //if( mpLogFont ) // eCharSet = mpLogFont->lfCharSet; pLayout = new Os2SalLayout( mhPS, 0, rFontFace, rFontInstance ); } if( mfFontScale != 1.0 ) pLayout->SetFontScale( mfFontScale ); return pLayout; } // ======================================================================= ImplOs2FontEntry::ImplOs2FontEntry( ImplFontSelectData& rFSD ) : ImplFontEntry( rFSD ), maWidthMap( 512 ) #ifdef GCP_KERN_HACK ,mpKerningPairs( NULL ) ,mnKerningPairs( -1 ) #endif // GCP_KERN_HACK { } // ----------------------------------------------------------------------- ImplOs2FontEntry::~ImplOs2FontEntry() { #ifdef GCP_KERN_HACK delete[] mpKerningPairs; #endif // GCP_KERN_HACK } // ----------------------------------------------------------------------- #ifdef GCP_KERN_HACK bool ImplOs2FontEntry::HasKernData() const { return (mnKerningPairs >= 0); } // ----------------------------------------------------------------------- void ImplOs2FontEntry::SetKernData( int nPairCount, const KERNINGPAIRS* pPairData ) { mnKerningPairs = nPairCount; mpKerningPairs = new KERNINGPAIRS[ mnKerningPairs ]; ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIRS) ); } // ----------------------------------------------------------------------- int ImplOs2FontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const { int nKernAmount = 0; if( mpKerningPairs ) { const KERNINGPAIRS aRefPair = { cLeft, cRight, 0 }; const KERNINGPAIRS* pFirstPair = mpKerningPairs; const KERNINGPAIRS* pEndPair = mpKerningPairs + mnKerningPairs; const KERNINGPAIRS* pPair = std::lower_bound( pFirstPair, pEndPair, aRefPair, ImplCmpKernData ); if( (pPair != pEndPair) && (pPair->sFirstChar == aRefPair.sFirstChar) && (pPair->sSecondChar == aRefPair.sSecondChar) ) nKernAmount = pPair->lKerningAmount; } return nKernAmount; } #endif // GCP_KERN_HACK // ======================================================================= ImplFontData* ImplOs2FontData::Clone() const { if( mpUnicodeMap ) mpUnicodeMap->AddReference(); ImplFontData* pClone = new ImplOs2FontData( *this ); return pClone; } // ----------------------------------------------------------------------- ImplFontEntry* ImplOs2FontData::CreateFontInstance( ImplFontSelectData& rFSD ) const { //debug_printf("ImplOs2FontData::CreateFontInstance\n"); ImplFontEntry* pEntry = new ImplOs2FontEntry( rFSD ); return pEntry; } // =======================================================================