diff -rup vcl_old/inc/vcl/outdev.hxx vcl/inc/vcl/outdev.hxx --- vcl_old/inc/vcl/outdev.hxx 2008-05-30 12:04:01.000000000 +0200 +++ vcl/inc/vcl/outdev.hxx 2008-06-13 10:48:38.798000000 +0200 @@ -1017,6 +1017,9 @@ public: xub_StrLen HasGlyphs( const Font& rFont, const String& rStr, xub_StrLen nIndex = 0, xub_StrLen nLen = STRING_LEN ) const; + + long GetMinKashida() const; + long GetMinKashida( const Font& rFont ) const; USHORT GetBitCount() const; diff -rup vcl_old/inc/vcl/outfont.hxx vcl/inc/vcl/outfont.hxx --- vcl_old/inc/vcl/outfont.hxx 2008-04-11 09:48:01.000000000 +0200 +++ vcl/inc/vcl/outfont.hxx 2008-06-13 10:48:38.819000000 +0200 @@ -315,6 +315,8 @@ public: // TODO: hide members behind acc long mnDStrikeoutSize; // Hoehe von doppelter Durchstreichung long mnDStrikeoutOffset1; // Offset von doppelter Durchstreichung zur Baseline long mnDStrikeoutOffset2; // Offset von doppelter Durchstreichung zur Baseline + long mnMinKashida; // Minimal width of kashida (Arabic) + }; // ----------------- diff -rup vcl_old/source/gdi/outdev3.cxx vcl/source/gdi/outdev3.cxx --- vcl_old/source/gdi/outdev3.cxx 2008-04-11 10:49:58.000000000 +0200 +++ vcl/source/gdi/outdev3.cxx 2008-06-13 10:49:38.138000000 +0200 @@ -114,6 +114,9 @@ using namespace ::vcl; // ======================================================================= + + + //#ifdef USE_NEW_RTL_IMPLEMENTATION @@ -3771,6 +3774,7 @@ ImplFontMetricData::ImplFontMetricData( mnDStrikeoutSize = 0; mnDStrikeoutOffset1 = 0; mnDStrikeoutOffset2 = 0; + mnMinKashida = 0; } // ----------------------------------------------------------------------- @@ -7401,6 +7405,29 @@ FontMetric OutputDevice::GetFontMetric( // ----------------------------------------------------------------------- +long OutputDevice::GetMinKashida() const +{ + DBG_TRACE( "OutputDevice::GetMinKashida()" ); + DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice ); + if( mbNewFont && !ImplNewFont() ) + return 0; + + ImplFontEntry* pEntry = mpFontEntry; + ImplFontMetricData* pMetric = &(pEntry->maMetric); + return ImplDevicePixelToLogicWidth( pMetric->mnMinKashida ); +} +// ----------------------------------------------------------------------- + +long OutputDevice::GetMinKashida( const Font& rFont ) const +{ + // select font, query Kashida, select original font again + Font aOldFont = GetFont(); + const_cast(this)->SetFont( rFont ); + long aKashida = GetMinKashida(); + const_cast(this)->SetFont( aOldFont ); + return aKashida; +} + // TODO: best is to get rid of this method completely ULONG OutputDevice::GetKerningPairCount() const { diff -rup vcl_old/win/inc/salgdi.h vcl/win/inc/salgdi.h --- vcl_old/win/inc/salgdi.h 2008-05-30 12:13:32.000000000 +0200 +++ vcl/win/inc/salgdi.h 2008-06-13 11:12:53.771000000 +0200 @@ -78,6 +78,7 @@ public: bool IsGlyphApiDisabled() const { return mbDisableGlyphApi; } bool SupportsKorean() const { return mbHasKoreanRange; } bool SupportsCJK() const { return mbHasCJKSupport; } + bool SupportsArabic() const { return mbHasArabicSupport; } bool AliasSymbolsHigh() const { return mbAliasSymbolsHigh; } bool AliasSymbolsLow() const { return mbAliasSymbolsLow; } @@ -96,6 +97,7 @@ private: mutable bool mbDisableGlyphApi; mutable bool mbHasKoreanRange; mutable bool mbHasCJKSupport; + mutable bool mbHasArabicSupport; mutable ImplFontCharMap* mpUnicodeMap; mutable const Ucs2SIntMap* mpEncodingVector; diff -rup vcl_old/win/source/gdi/salgdi3.cxx vcl/win/source/gdi/salgdi3.cxx --- vcl_old/win/source/gdi/salgdi3.cxx 2008-04-11 12:48:39.000000000 +0200 +++ vcl/win/source/gdi/salgdi3.cxx 2008-06-13 11:15:41.799000000 +0200 @@ -36,6 +36,7 @@ #include #include #include +#include #ifndef _OSL_FILE_HXX #include #endif @@ -65,6 +66,8 @@ #endif #include +#include + #ifdef GCP_KERN_HACK #include #endif @@ -111,6 +114,47 @@ static bool bImplSalCourierNew = false; // ======================================================================= +// ----------------------------------------------------------------------- +// dynamic loading of usp library +// copied from winlayout.cxx + +static oslModule aUspModule = NULL; +static bool bUspEnabled = true; + +static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* )); +static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* )); + +static bool InitUSP() +{ + rtl::OUString aLibraryName( RTL_CONSTASCII_USTRINGPARAM( "usp10" ) ); + aUspModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT ); + if( !aUspModule ) + return (bUspEnabled = false); + + + rtl::OUString queryFuncName = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ScriptGetFontProperties" ) ); + pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*)) + osl_getSymbol( aUspModule, queryFuncName.pData ); + bUspEnabled &= (NULL != pScriptGetFontProperties); + + queryFuncName = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ScriptFreeCache" ) ); + pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*)) + osl_getSymbol( aUspModule, queryFuncName.pData ); + bUspEnabled &= (NULL != pScriptFreeCache); + + if( !bUspEnabled ) + { + osl_unloadModule( aUspModule ); + aUspModule = NULL; + } + return bUspEnabled; +} + + + + + + // TODO: also support temporary TTC font files typedef std::map< String, ImplDevFontAttributes > FontAttrMap; @@ -740,6 +784,7 @@ ImplWinFontData::ImplWinFontData( const mbDisableGlyphApi( false ), mbHasKoreanRange( false ), mbHasCJKSupport( false ), + mbHasArabicSupport ( false ), mbAliasSymbolsLow( false ), mbAliasSymbolsHigh( false ), mnId( 0 ), @@ -870,6 +915,7 @@ void ImplWinFontData::ReadOs2Table( HDC mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000); mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000) | (ulUnicodeRange2 & 0x01100000); + mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000); } } @@ -1467,6 +1513,24 @@ void WinSalGraphics::GetFontMetric( Impl if( mpWinFontData[0]->SupportsKorean() ) pMetric->mnDescent += pMetric->mnExtLeading; } + + if( mpWinFontData[0] && mpWinFontData[0]->SupportsArabic() ) + { + if (aUspModule || (bUspEnabled && InitUSP()) ) + { + //ScriptGetFontProperties + SCRIPT_FONTPROPERTIES aFontProperties; + SCRIPT_CACHE aScriptCache = NULL; // should use ScriptCache from ImplWinFontEntry + // but how do I get there + aFontProperties.cBytes = sizeof (aFontProperties); + HRESULT nRC = (*pScriptGetFontProperties)( mhDC, &aScriptCache, &aFontProperties ); + if( !nRC ) + { + pMetric->mnMinKashida = static_cast( mfFontScale * aFontProperties.iKashidaWidth); + } + (*pScriptFreeCache)( &aScriptCache ); + } + } } // ----------------------------------------------------------------------- diff -rup vcl_old/win/source/gdi/winlayout.cxx vcl/win/source/gdi/winlayout.cxx --- vcl_old/win/source/gdi/winlayout.cxx 2008-05-14 11:52:04.000000000 +0200 +++ vcl/win/source/gdi/winlayout.cxx 2008-06-13 11:18:19.641000000 +0200 @@ -1105,6 +1105,9 @@ private: GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos + + // font specific info + int mnMinKashidaWidth; // minimal Kashida width allowed by font }; // ----------------------------------------------------------------------- @@ -1670,11 +1673,23 @@ bool UniscribeLayout::LayoutText( ImplLa } } + //ScriptGetFontProperties + + SCRIPT_FONTPROPERTIES aFontProperties; + aFontProperties.cBytes = sizeof (aFontProperties); // don't forget to initialize + mnMinKashidaWidth = 0; + HRESULT nRC = (*pScriptGetFontProperties)( mhDC, &rScriptCache, &aFontProperties ); + if( !nRC ) + { + mnMinKashidaWidth = aFontProperties.iKashidaWidth; + } + + // scale layout metrics if needed if( mfFontScale != 1.0 ) { mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale); - + mnMinKashidaWidth = (int)((double)mnMinKashidaWidth*mfFontScale); for( i = 0; i < mnItemCount; ++i ) mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale); @@ -2342,16 +2357,18 @@ void UniscribeLayout::ApplyDXArray( cons if( rVisualItem.mpScriptItem->a.fRTL ) { for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) - if ( (1U << mpVisualAttrs[i].uJustification) & 0x7F89 ) // any Arabic justification ? - { - // yes + if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF89 ) // any Arabic justification ? + { // the last SCRIPT_JUSTIFY_xxx + // yes // == 15 (usp 1.6) bHasKashida = true; break; } if ( bHasKashida ) for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) { - if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE ) + // check if we still need this hack after correction of kashida placing? + // (i87688): apparently yes, we still need it! + if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE ) // usp decided that justification can't be applied here // but maybe our Kashida algorithm thinks differently. // To avoid trouble (gaps within words, last character of @@ -2385,6 +2402,15 @@ void UniscribeLayout::ApplyDXArray( cons break; } + // to prepare for the next visual item + // update nXOffset to the next items position + // before the mpJustifications[] array gets modified + int nMinGlyphPos, nEndGlyphPos; + if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) + { + for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) + nXOffset += mpJustifications[ i ]; + // TODO: for kashida justification // check the widths which are added to mpJustification // if added width is smaller than iKashidaWidth returned by @@ -2393,13 +2419,60 @@ void UniscribeLayout::ApplyDXArray( cons // Need to think of a way to compensate the change in overall // width. - // to prepare for the next visual item - // update nXOffset to the next items position - // before the mpJustifications[] array gets modified - int nMinGlyphPos, nEndGlyphPos; - if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) ) - for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) - nXOffset += mpJustifications[ i ]; + if ( bHasKashida && mnMinKashidaWidth ) + { + int nSpaceAdded; + for ( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) + { + nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; + if ( nSpaceAdded && ((1U << mpVisualAttrs[i].uJustification) & 0xFF89 )) + { + if ( i > nMinGlyphPos && ! mpGlyphAdvances [ i - 1 ] && nSpaceAdded >= mnMinKashidaWidth) + { + // vowel, we do it like ScriptJustify does + // this works great !!! + mpJustifications [ i ] = mpGlyphAdvances [ i ]; + mpJustifications [ i - 1 ] += nSpaceAdded; + } + + if ( nSpaceAdded < mnMinKashidaWidth ) + { + mpJustifications[ i ] = mpGlyphAdvances[ i ]; // overriding; + /*for ( int j = i; j >= nMinGlyphPos; --j ) + { + if ( mpVisualAttrs[ j ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK ) + { + mpJustifications [ j ] += nSpaceAdded; // padding a blank instead + nSpaceAdded = 0; + break; + } + }*/ + + // no blank found + // try the other side + for ( int j = i; j < nEndGlyphPos; ++j ) + { + if ( mpVisualAttrs[ j ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK ) + { + mpJustifications [ j ] += nSpaceAdded; // padding a blank instead + nSpaceAdded = 0; + break; + } + } + + if ( nSpaceAdded ) + { + // no blank found + // right align + // rVisualItem.mnXOffset += nSpaceAdded; + } + } + } + } + } + } + + // right align the justification-adjusted glyphs in their cells for RTL-items if( bManualCellAlign && rVisualItem.mpScriptItem->a.fRTL && !bHasKashida ) @@ -2454,6 +2527,7 @@ void UniscribeLayout::Justify( long nNew nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5); SCRIPT_FONTPROPERTIES aFontProperties; + aFontProperties.cBytes = sizeof (aFontProperties); // don't forget to initialize int nMinKashida = 1; HRESULT nRC = (*pScriptGetFontProperties)( mhDC, &rScriptCache, &aFontProperties ); if( !nRC )