blob: a1f28ea8a9ba8578ba7265fe94485c410dc91143 [file] [log] [blame]
/**************************************************************
*
* 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 <svpm.h>
#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 <os2/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, 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<Os2SalGraphics&>(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<Os2SalGraphics&>(rGraphics).mbPrinter) {
// convert to codepage
ByteString str( mpOutGlyphs, gsl_getSystemTextEncoding() );
// gliph size is not recalculated, so it could be wrong!
rc = Ft2CharStringPosAtA( static_cast<Os2SalGraphics&>(rGraphics).mhPS,
&aPt, NULL, CHS_VECTOR, mnGlyphCount, (PSZ)str.GetBuffer(),
(LONG*)mpGlyphAdvances, 0);
} else {
// try unicode rendering to screen
rc = Ft2CharStringPosAtW( static_cast<Os2SalGraphics&>(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<Os2SalGraphics&>(rGraphics).mhPS,str.GetBuffer());
#endif
// gliph size is not recalculated, so it could be wrong!
rc = Ft2CharStringPosAtA( static_cast<Os2SalGraphics&>(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;
}
// =======================================================================