diff options
author | Vladimir Glazounov <vg@openoffice.org> | 2007-09-20 15:06:58 +0000 |
---|---|---|
committer | Vladimir Glazounov <vg@openoffice.org> | 2007-09-20 15:06:58 +0000 |
commit | b820d1bf41ba8cd3b44e735f569708be3068dc78 (patch) | |
tree | 18ebcc0885ab936fb51d7c7310177d9810aa3ced /vcl/os2/source/gdi/os2layout.cxx | |
parent | bf45361780a5a17e3b0bebd5d16a5a54497d0132 (diff) |
INTEGRATION: CWS os2port01 (1.1.2); FILE ADDED
2006/11/29 14:34:01 ydario 1.1.2.1: Initial OS/2 import.
Diffstat (limited to 'vcl/os2/source/gdi/os2layout.cxx')
-rw-r--r-- | vcl/os2/source/gdi/os2layout.cxx | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/vcl/os2/source/gdi/os2layout.cxx b/vcl/os2/source/gdi/os2layout.cxx new file mode 100644 index 000000000000..c9c5c0c62f8a --- /dev/null +++ b/vcl/os2/source/gdi/os2layout.cxx @@ -0,0 +1,1097 @@ +/************************************************************************* + * + * $RCSfile: os2layout.cxx,v $ + * + * $Revision: 1.2 $ + * + * last change: $Author: vg $ $Date: 2007-09-20 16:06:58 $ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * - Sun Industry Standards Source License Version 1.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Sun Industry Standards Source License Version 1.1 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source License Version 1.1 (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.openoffice.org/license.html. + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +#ifndef _SVWIN_H +#include <tools/svwin.h> +#endif + +#include <rtl/ustring.hxx> +#include <osl/module.h> + +#ifndef _SV_SALGDI_H +#include <salgdi.h> +#endif // _SV_SALGDI_HXX +#ifndef _SV_SALDATA_HXX +#include <saldata.hxx> +#endif // _SV_SALDATA_HXX + +#ifndef _SV_SALLAYOUT_H +#include <sallayout.h> +#endif // _SV_SALLAYOUT_H + +#ifndef __H_FT2LIB +#include <wingdi.h> +#include <ft2lib.h> +#endif + +#include <cstdio> +#include <malloc.h> + +#ifdef GCP_KERN_HACK + #include <algorithm> +#endif // GCP_KERN_HACK + +#include <hash_map> +typedef std::hash_map<int,int> 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, 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 + + ImplOs2FontData& mrOs2FontData; + ImplOs2FontEntry& mrOs2FontEntry; +}; + +// ======================================================================= + +class Os2SalLayout : public Os2Layout +{ +public: + Os2SalLayout( HPS, BYTE nCharSet, ImplOs2FontData&, ImplOs2FontEntry& ); + virtual ~Os2SalLayout(); + + virtual bool LayoutText( ImplLayoutArgs& ); + virtual void AdjustLayout( ImplLayoutArgs& ); + virtual void DrawText( SalGraphics& ) const; + + virtual int GetNextGlyphs( int nLen, long* pGlyphs, Point& rPos, int&, + long* 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; + BYTE mnCharSet; + +}; + +// ======================================================================= + +Os2Layout::Os2Layout( HPS hPS, ImplOs2FontData& rWFD, ImplOs2FontEntry& rWFE ) +: mhPS( hPS ), + mnBaseAdv( 0 ), + mfFontScale( 1.0 ), + mrOs2FontData( rWFD ), + mrOs2FontEntry( rWFE ) +{ + 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, BYTE nCharSet, + 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, long* pGlyphs, Point& rPos, int& nStart, + long* 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 {nGlyphIndex,nCharPos,nGlyphAdvance} + long nGlyphIndex = mpOutGlyphs[ nStart ]; + if( mbDisableGlyphs ) + { + if( mnLayoutFlags & SAL_LAYOUT_VERTICAL ) + { + sal_Unicode cChar = (sal_Unicode)(nGlyphIndex & GF_IDXMASK); +#ifdef GNG_VERT_HACK + if( mrOs2FontData.HasGSUBstitutions( mhPS ) + && mrOs2FontData.IsGSUBstituted( cChar ) ) + nGlyphIndex |= GF_ROTL | GF_GSUB; + else +#endif // GNG_VERT_HACK + { + nGlyphIndex |= GetVerticalFlags( cChar ); + if( !(nGlyphIndex & GF_ROTMASK) ) + nGlyphIndex |= GF_VERT; + } + } + nGlyphIndex |= GF_ISCHAR; + } + ++nCount; + *(pGlyphs++) = nGlyphIndex; + 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<Os2SalGraphics&>(rGraphics).mnHeight - aPos.Y(); + + rc = Ft2CharStringPosAtW( static_cast<Os2SalGraphics&>(rGraphics).mhPS, + &aPt, NULL, CHS_VECTOR, mnGlyphCount, (LPWSTR)mpOutGlyphs, + (LONG*)mpGlyphAdvances, 0); + // Ft2* fails if the selected font doesn't have an Unicode charmap + if (rc == GPI_ERROR) { + ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() ); + // gliph size is not recalculated, so it could be wrong! + rc = Ft2CharStringPosAtA( static_cast<Os2SalGraphics&>(rGraphics).mhPS, + &aPt, NULL, 0, mnGlyphCount, (PSZ)str.GetBuffer(), + (LONG*)mpGlyphAdvances, 0); + } +#if 0 // unicode rendering using codepage 1200 + { + ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() ); + debug_printf("Os2SalLayout::DrawText HPS %08x\n",static_cast<Os2SalGraphics&>(rGraphics).mhPS); + if (str.GetBuffer()) + debug_printf("Os2SalLayout::DrawText this %08x, '%s'\n",this,str.GetBuffer()); + } + + rc = GpiSetCp( static_cast<Os2SalGraphics&>(rGraphics).mhPS, 1200); + debug_printf("Os2SalLayout::DrawText GpiSetCp '%d'\n", rc); + rc = GpiCharStringPosAt( static_cast<Os2SalGraphics&>(rGraphics).mhPS, + &aPt, NULL, 0, mnGlyphCount, (PSZ)mpOutGlyphs, + (LONG*)mpGlyphAdvances); + debug_printf("Os2SalLayout::DrawText %d '%s'\n", rc, str.GetBuffer()); +#endif +} + +// ----------------------------------------------------------------------- + +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"); + + 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 + + //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; +} + +// ======================================================================= + |