diff options
author | Jens-Heiner Rechtien <hr@openoffice.org> | 2009-01-05 15:33:41 +0000 |
---|---|---|
committer | Jens-Heiner Rechtien <hr@openoffice.org> | 2009-01-05 15:33:41 +0000 |
commit | 347a1622743363ae11cd12bc00d5827c5c952f79 (patch) | |
tree | cec8eee1bab3c33c4154ed08dd3066ad55679f81 /vcl | |
parent | 50afe624b996edb05856bfe1be390639f458a88e (diff) |
CWS-TOOLING: integrate CWS kashidafix
2008-12-15 15:31:40 +0100 hde r265507 : #i97098#
2008-12-15 15:30:52 +0100 hde r265506 : #i97098#
2008-12-10 14:08:07 +0100 fredrikh r265184 : i97098
2008-11-27 15:07:01 +0100 hdu r264493 : #i60594# only determine GetNextGlyphs() charpos if requested+available
2008-11-27 14:09:42 +0100 hdu r264487 : #i60594# simplify RTL-glyph-injection also for manual-cell-aligned cases
2008-11-26 13:25:08 +0100 fme r264379 : #i60594# Kashida fixes - syntax error
2008-11-26 13:16:22 +0100 hdu r264374 : #i60594# allow glyph injection even if there is not enough room if they can overlap
2008-11-25 16:40:20 +0100 hdu r264314 : #i60594# fix glyph-injection for PDF-export for usp>=1.6
2008-11-24 16:17:11 +0100 hdu r264254 : #i71804# adjust glyph-fallback usp-methods for new glyph-injection infrastructure
2008-11-24 16:15:30 +0100 hdu r264253 : #i71804# disable glyph-injection for glyph-fallback mixing
2008-11-20 08:29:15 +0100 fme r264027 : #i60594# Fix correction
2008-11-14 10:10:54 +0100 fme r263666 : CWS-TOOLING: rebase CWS kashidafix to trunk@263288 (milestone: DEV300:m35)
2008-10-30 16:35:30 +0100 fme r262834 : #i60594# migrate cws kashidafix to SVN.
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/inc/vcl/outdev.hxx | 15 | ||||
-rw-r--r-- | vcl/inc/vcl/outfont.hxx | 12 | ||||
-rwxr-xr-x | vcl/inc/vcl/sallayout.hxx | 4 | ||||
-rw-r--r-- | vcl/source/gdi/outdev3.cxx | 59 | ||||
-rwxr-xr-x | vcl/source/gdi/sallayout.cxx | 7 | ||||
-rw-r--r-- | vcl/win/inc/salgdi.h | 7 | ||||
-rw-r--r-- | vcl/win/source/gdi/salgdi3.cxx | 18 | ||||
-rwxr-xr-x | vcl/win/source/gdi/winlayout.cxx | 425 |
8 files changed, 481 insertions, 66 deletions
diff --git a/vcl/inc/vcl/outdev.hxx b/vcl/inc/vcl/outdev.hxx index 578eb2b787a9..98c096087a66 100644 --- a/vcl/inc/vcl/outdev.hxx +++ b/vcl/inc/vcl/outdev.hxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: outdev.hxx,v $ - * $Revision: 1.11.28.1 $ + * $Revision: 1.7.20.4 $ * * This file is part of OpenOffice.org. * @@ -1032,6 +1032,19 @@ 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; + + // i60594 + // validate kashida positions against the current font + // returns count of invalid kashida positions + xub_StrLen ValidateKashidas ( const String& rTxt, + xub_StrLen nIdx, xub_StrLen nLen, + xub_StrLen nKashCount, // number of suggested kashida positions (in) + const xub_StrLen* pKashidaPos, // suggested kashida positions (in) + xub_StrLen* pKashidaPosDropped // invalid kashida positions (out) + ) const; + USHORT GetBitCount() const; BOOL GetTextIsRTL( const String&, xub_StrLen nIndex, diff --git a/vcl/inc/vcl/outfont.hxx b/vcl/inc/vcl/outfont.hxx index 44c0975cd8f2..86a5e59f6345 100644 --- a/vcl/inc/vcl/outfont.hxx +++ b/vcl/inc/vcl/outfont.hxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: outfont.hxx,v $ - * $Revision: 1.6 $ + * $Revision: 1.6.14.2 $ * * This file is part of OpenOffice.org. * @@ -279,17 +279,25 @@ public: void ImplInitAboveTextLineSize(); public: // TODO: hide members behind accessor methods + // font instance attributes from the font request long mnWidth; // Reference Width + short mnOrientation; // Rotation in 1/10 degrees + + // font metrics measured for the font instance long mnAscent; // Ascent long mnDescent; // Descent long mnIntLeading; // Internal Leading long mnExtLeading; // External Leading int mnSlant; // Slant (Italic/Oblique) + long mnMinKashida; // Minimal width of kashida (Arabic) + + // font attributes queried from the font instance int meFamilyType; // Font Family Type - short mnOrientation; // Rotation in 1/10 degrees bool mbDevice; // Flag for Device Fonts bool mbScalableFont; bool mbKernableFont; + + // font metrics that are usually derived from the measurements long mnUnderlineSize; // Lineheight of Underline long mnUnderlineOffset; // Offset from Underline to Baseline long mnBUnderlineSize; // Hoehe von fetter Unterstreichung diff --git a/vcl/inc/vcl/sallayout.hxx b/vcl/inc/vcl/sallayout.hxx index 6e736de87526..d40dde9014bc 100755 --- a/vcl/inc/vcl/sallayout.hxx +++ b/vcl/inc/vcl/sallayout.hxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: sallayout.hxx,v $ - * $Revision: 1.8 $ + * $Revision: 1.8.54.1 $ * * This file is part of OpenOffice.org. * @@ -206,6 +206,7 @@ public: virtual long FillDXArray( sal_Int32* pDXArray ) const = 0; virtual long GetTextWidth() const { return FillDXArray( NULL ); } virtual void GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const = 0; + virtual bool IsKashidaPosValid ( int /*nCharPos*/ ) const { return true; } // i60594 // methods using glyph indexing virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdAry, Point& rPos, int&, @@ -223,6 +224,7 @@ public: virtual void MoveGlyph( int nStart, long nNewXPos ) = 0; virtual void DropGlyph( int nStart ) = 0; virtual void Simplify( bool bIsBase ) = 0; + virtual void DisableGlyphInjection( bool /*bDisable*/ ) {} protected: // used by layout engines diff --git a/vcl/source/gdi/outdev3.cxx b/vcl/source/gdi/outdev3.cxx index 19a9778ebb04..60db5cc152c2 100644 --- a/vcl/source/gdi/outdev3.cxx +++ b/vcl/source/gdi/outdev3.cxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: outdev3.cxx,v $ - * $Revision: 1.245 $ + * $Revision: 1.240.14.5 $ * * This file is part of OpenOffice.org. * @@ -3734,9 +3734,11 @@ void OutputDevice::ImplInitAboveTextLineSize() ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData ) : ImplFontAttributes( rFontSelData ) { + // initialize the members provided by the font request mnWidth = rFontSelData.mnWidth; mnOrientation = sal::static_int_cast<short>(rFontSelData.mnOrientation); + // intialize the used font name if( rFontSelData.mpFontData ) { maName = rFontSelData.mpFontData->maName; @@ -3753,12 +3755,15 @@ ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData ) mbKernableFont = false; } + // reset metrics that are usually measured for the font instance mnAscent = 0; mnDescent = 0; mnIntLeading = 0; mnExtLeading = 0; mnSlant = 0; + mnMinKashida = 0; + // reset metrics that are usually derived from the measurements mnUnderlineSize = 0; mnUnderlineOffset = 0; mnBUnderlineSize = 0; @@ -7557,6 +7562,58 @@ FontMetric OutputDevice::GetFontMetric( const Font& rFont ) const // ----------------------------------------------------------------------- +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<OutputDevice*>(this)->SetFont( rFont ); + long aKashida = GetMinKashida(); + const_cast<OutputDevice*>(this)->SetFont( aOldFont ); + return aKashida; +} + +// ----------------------------------------------------------------------- +xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt, + xub_StrLen nIdx, xub_StrLen nLen, + xub_StrLen nKashCount, + const xub_StrLen* pKashidaPos, + xub_StrLen* pKashidaPosDropped ) const +{ + // do layout + SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen ); + if( !pSalLayout ) + return 0; + xub_StrLen nDropped = 0; + for( int i = 0; i < nKashCount; ++i ) + { + if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] )) + { + pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ]; + ++nDropped; + } + } + pSalLayout->Release(); + return nDropped; +} + + + +// ----------------------------------------------------------------------- + + // TODO: best is to get rid of this method completely ULONG OutputDevice::GetKerningPairCount() const { diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx index 9e02b42aa789..9dd0bfdc0197 100755 --- a/vcl/source/gdi/sallayout.cxx +++ b/vcl/source/gdi/sallayout.cxx @@ -1715,6 +1715,9 @@ void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) } mpLayouts[n]->AdjustLayout( aMultiArgs ); + // disable glyph-injection for glyph-fallback SalLayout iteration + mpLayouts[n]->DisableGlyphInjection( true ); + // remove unused parts of component if( n > 0 ) { @@ -1893,6 +1896,10 @@ void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs ) } mpLayouts[0]->Simplify( true ); + + // reenable glyph-injection + for( n = 0; n < mnLevel; ++n ) + mpLayouts[n]->DisableGlyphInjection( false ); } // ----------------------------------------------------------------------- diff --git a/vcl/win/inc/salgdi.h b/vcl/win/inc/salgdi.h index cea6fc5dbbb3..f9d4681e0e6e 100644 --- a/vcl/win/inc/salgdi.h +++ b/vcl/win/inc/salgdi.h @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: salgdi.h,v $ - * $Revision: 1.32 $ + * $Revision: 1.30.20.5 $ * * This file is part of OpenOffice.org. * @@ -52,7 +52,7 @@ class ImplFontAttrCache; #define PALRGB_TO_RGB(nPalRGB) ((nPalRGB)&0x00ffffff) // win32 platform specific options. Move them to the PMK file? -#define USE_UNISCRIBE + #define GCP_KERN_HACK #define GNG_VERT_HACK @@ -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; @@ -337,6 +339,7 @@ public: bool bVertical, Int32Vector& rWidths, Ucs2UIntMap& rUnicodeEnc ); + virtual int GetMinKashidaWidth(); virtual BOOL GetGlyphBoundRect( long nIndex, Rectangle& ); virtual BOOL GetGlyphOutline( long nIndex, ::basegfx::B2DPolyPolygon& ); diff --git a/vcl/win/source/gdi/salgdi3.cxx b/vcl/win/source/gdi/salgdi3.cxx index 4511ff0c3fef..de08f1c25b50 100644 --- a/vcl/win/source/gdi/salgdi3.cxx +++ b/vcl/win/source/gdi/salgdi3.cxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: salgdi3.cxx,v $ - * $Revision: 1.97 $ + * $Revision: 1.95.14.5 $ * * This file is part of OpenOffice.org. * @@ -33,6 +33,7 @@ #include <string.h> #include <malloc.h> +#include <osl/module.h> #include <tools/svwin.h> #include <rtl/logfile.hxx> #include <rtl/tencinfo.h> @@ -72,14 +73,13 @@ #include <tools/stream.hxx> #include <rtl/bootstrap.hxx> - #include <vector> #include <set> -#ifndef INCLUDED_MAP +//#ifndef INCLUDED_MAP #include <map> -#define INCLUDED_MAP -#endif +//#define INCLUDED_MAP +//#endif static const int MAXFONTHEIGHT = 2048; @@ -111,6 +111,8 @@ static bool bImplSalCourierNew = false; // ======================================================================= +// ----------------------------------------------------------------------- + // TODO: also support temporary TTC font files typedef std::map< String, ImplDevFontAttributes > FontAttrMap; @@ -806,6 +808,7 @@ ImplWinFontData::ImplWinFontData( const ImplDevFontAttributes& rDFS, mbDisableGlyphApi( false ), mbHasKoreanRange( false ), mbHasCJKSupport( false ), + mbHasArabicSupport ( false ), mbAliasSymbolsLow( false ), mbAliasSymbolsHigh( false ), mnId( 0 ), @@ -936,7 +939,8 @@ void ImplWinFontData::ReadOs2Table( HDC hDC ) const mbHasCJKSupport = (ulUnicodeRange2 & 0x2DF00000); mbHasKoreanRange= (ulUnicodeRange1 & 0x10000000) | (ulUnicodeRange2 & 0x01100000); - } + mbHasArabicSupport = (ulUnicodeRange1 & 0x00002000); + } } // ----------------------------------------------------------------------- @@ -1521,6 +1525,8 @@ void WinSalGraphics::GetFontMetric( ImplFontMetricData* pMetric ) if( mpWinFontData[0]->SupportsKorean() ) pMetric->mnDescent += pMetric->mnExtLeading; } + + pMetric->mnMinKashida = GetMinKashidaWidth(); } // ----------------------------------------------------------------------- diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx index 36cb76800eac..26cdc9cd7587 100755 --- a/vcl/win/source/gdi/winlayout.cxx +++ b/vcl/win/source/gdi/winlayout.cxx @@ -7,7 +7,7 @@ * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: winlayout.cxx,v $ - * $Revision: 1.115 $ + * $Revision: 1.113.6.9 $ * * This file is part of OpenOffice.org. * @@ -54,6 +54,7 @@ // for GetMirroredChar #include <vcl/svapp.hxx> +#define USE_UNISCRIBE #ifdef USE_UNISCRIBE #include <Usp10.h> #include <ShLwApi.h> @@ -102,8 +103,15 @@ private: public: int GetCachedGlyphWidth( int nCharCode ) const; void CacheGlyphWidth( int nCharCode, int nCharWidth ); + + bool InitKashidaHandling( HDC ); + int GetMinKashidaWidth() const { return mnMinKashidaWidth; } + int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; } + private: IntMap maWidthMap; + mutable int mnMinKashidaWidth; + mutable int mnMinKashidaGlyph; }; // ----------------------------------------------------------------------- @@ -545,6 +553,8 @@ bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs ) } // scale layout metrics if needed + // TODO: does it make the code more simple if the metric scaling + // is moved to the methods that need metric scaling (e.g. FillDXArray())? if( mfFontScale != 1.0 ) { mnWidth = (long)(mnWidth * mfFontScale); @@ -886,7 +896,7 @@ void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) nOldWidth += mpGlyphAdvances[ j ]; int nDiff = nOldWidth - pDXArray[ i ]; - // disabled because of #104768# + // 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 @@ -1046,11 +1056,15 @@ public: //long mnPixelWidth; int mnXOffset; ABC maABCWidths; + bool mbHasKashidas; public: bool IsEmpty() const { return (mnEndGlyphPos <= 0); } + bool HasKashidas() const { return mbHasKashidas; } }; +// ----------------------------------------------------------------------- + class UniscribeLayout : public WinLayout { public: @@ -1065,11 +1079,13 @@ public: virtual long FillDXArray( long* pDXArray ) const; virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const; virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const; + virtual bool IsKashidaPosValid ( int nCharPos ) const; // for glyph+font+script fallback virtual void MoveGlyph( int nStart, long nNewXPos ); virtual void DropGlyph( int nStart ); virtual void Simplify( bool bIsBase ); + virtual void DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; } protected: virtual ~UniscribeLayout(); @@ -1103,6 +1119,15 @@ 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 + + // kashida stuff + void InitKashidaHandling(); + void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos ); + bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos ); + + int mnMinKashidaWidth; + int mnMinKashidaGlyph; + bool mbDisableGlyphInjection; }; // ----------------------------------------------------------------------- @@ -1243,21 +1268,23 @@ static bool InitUSP() UniscribeLayout::UniscribeLayout( HDC hDC, const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry ) : WinLayout( hDC, rWinFontData, rWinFontEntry ), - mnItemCount(0), + mnItemCount( 0 ), mpScriptItems( NULL ), mpVisualItems( NULL ), mpLogClusters( NULL ), mpCharWidths( NULL ), mnCharCapacity( 0 ), mnSubStringMin( 0 ), - mnGlyphCapacity(0), + mnGlyphCapacity( 0 ), mnGlyphCount( 0 ), mpOutGlyphs( NULL ), mpGlyphAdvances( NULL ), mpJustifications( NULL ), mpGlyphOffsets( NULL ), mpVisualAttrs( NULL ), - mpGlyphs2Chars( NULL ) + mpGlyphs2Chars( NULL ), + mnMinKashidaGlyph( 0 ), + mbDisableGlyphInjection( false ) {} // ----------------------------------------------------------------------- @@ -1669,6 +1696,8 @@ bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs ) } // scale layout metrics if needed + // TODO: does it make the code more simple if the metric scaling + // is moved to the methods that need metric scaling (e.g. FillDXArray())? if( mfFontScale != 1.0 ) { mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale); @@ -1757,9 +1786,16 @@ bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem, // ----------------------------------------------------------------------- int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, - int& nStart, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const + int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const { - if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs + // HACK to allow fake-glyph insertion (e.g. for kashidas) + // TODO: use iterator idiom instead of GetNextGlyphs(...) + // TODO: else make sure that the limit for glyph injection is sufficient (currently 256) + int nSubIter = nStartx8 & 0xff; + int nStart = nStartx8 >> 8; + + // check the glyph iterator + if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs return 0; // find the visual item for the nStart glyph position @@ -1788,9 +1824,9 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, } // after the last visual item there are no more glyphs - if( nItem >= mnItemCount ) + if( (nItem >= mnItemCount) || (nStart < 0) ) { - nStart = mnGlyphCount + 1; + nStartx8 = (mnGlyphCount + 1) << 8; return 0; } @@ -1809,7 +1845,10 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" ); if( !bRC ) + { + nStartx8 = (mnGlyphCount + 1) << 8; return 0; + } // make sure nStart is inside the range of relevant glyphs if( nStart < nMinGlyphPos ) @@ -1880,16 +1919,72 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int nCount = 0; while( nCount < nLen ) { + // prepare return values + sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ]; + int nGlyphWidth = pGlyphWidths[ nStart ]; + int nCharPos = -1; // no need to determine charpos + if( mpGlyphs2Chars ) // unless explicitly requested+provided + nCharPos = mpGlyphs2Chars[ nStart ]; + + // inject kashida glyphs if needed + if( !mbDisableGlyphInjection + && mpJustifications + && mnMinKashidaWidth + && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL ) + { + // prepare draw position adjustment + int nExtraOfs = (nSubIter++) * mnMinKashidaWidth; + // calculate space available for the injected glyphs + nGlyphWidth = mpGlyphAdvances[ nStart ]; + const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth; + const int nToFillWidth = nExtraWidth - nExtraOfs; + if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room + || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others + { + // handle if there is not sufficient room for a full glyph + if( nToFillWidth < mnMinKashidaWidth ) + { + // overlap it with the previously injected glyph if possible + int nOverlap = mnMinKashidaWidth - nToFillWidth; + // else overlap it with both neighboring glyphs + if( nSubIter <= 1 ) + nOverlap /= 2; + nExtraOfs -= nOverlap; + } + nGlyphWidth = mnMinKashidaWidth; + aGlyphId = mnMinKashidaGlyph; + nCharPos = -1; + } + else + { + nExtraOfs += nToFillWidth; // at right of cell + nSubIter = 0; // done with glyph injection + } + if( !bManualCellAlign ) + nExtraOfs -= nExtraWidth; // adjust for right-aligned cells + + // adjust the draw position for the injected-glyphs case + if( nExtraOfs ) + { + aRelativePos.X() += nExtraOfs; + rPos = GetDrawPosition( aRelativePos ); + } + } + // update return values - *(pGlyphs++) = mpOutGlyphs[ nStart ]; + *(pGlyphs++) = aGlyphId; if( pGlyphAdvances ) - *(pGlyphAdvances++) = pGlyphWidths[ nStart ]; + *(pGlyphAdvances++) = nGlyphWidth; if( pCharPosAry ) - *(pCharPosAry++) = mpGlyphs2Chars[ nStart ]; + *(pCharPosAry++) = nCharPos; // increment counter of returned glyphs ++nCount; + // reduce code complexity by returning early in glyph-injection case + if( nSubIter != 0 ) + break; + // stop after the last visible glyph in this visual item if( ++nStart >= nEndGlyphPos ) { @@ -1899,7 +1994,7 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, // RTL-justified glyph positioning is not easy // simplify the code by just returning only one glyph at a time - if( mpJustifications && !bManualCellAlign && pVI->mpScriptItem->a.fRTL ) + if( mpJustifications && pVI->mpScriptItem->a.fRTL ) break; // stop when the x-position of the next glyph is unexpected @@ -1914,13 +2009,16 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, } ++nStart; + nStartx8 = (nStart << 8) + nSubIter; return nCount; } // ----------------------------------------------------------------------- -void UniscribeLayout::MoveGlyph( int nStart, long nNewXPos ) +void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos ) { + DBG_ASSERT( (nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" ); + int nStart = nStartx8 >> 8; if( nStart > mnGlyphCount ) return; @@ -1940,14 +2038,9 @@ void UniscribeLayout::MoveGlyph( int nStart, long nNewXPos ) for( int i = mnItemCount; --i >= 0; ++pVI ) if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) ) break; - #if OSL_DEBUG_LEVEL > 0 - bool bRC = - #endif - GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); + bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ); + (void)bRC; // avoid var-not-used warning DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" ); - #if OSL_DEBUG_LEVEL > 0 - (void)bRC; - #endif } long nDelta = nNewXPos - pVI->mnXOffset; @@ -1968,13 +2061,15 @@ void UniscribeLayout::MoveGlyph( int nStart, long nNewXPos ) // ----------------------------------------------------------------------- -void UniscribeLayout::DropGlyph( int nStart ) +void UniscribeLayout::DropGlyph( int nStartx8 ) { + DBG_ASSERT( (nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" ); + int nStart = nStartx8 >> 8; DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" ); if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1 --nStart; - else // if( !nStart ) // nStart==0 for first visible glyph + else // nStart<=0 for first visible glyph { const VisualItem* pVI = mpVisualItems; for( int i = mnItemCount, nDummy; --i >= 0; ++pVI ) @@ -2336,19 +2431,25 @@ void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) || (rVisualItem.mnEndCharPos <= mnMinCharPos) ) continue; - bool bHasKashida = false; + // if needed prepare special handling for arabic justification + rVisualItem.mbHasKashidas = false; if( rVisualItem.mpScriptItem->a.fRTL ) { for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) - if ( (1U << mpVisualAttrs[i].uJustification) & 0x7F89 ) // any Arabic justification ? - { - // yes - bHasKashida = true; + if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF89 ) // any Arabic justification ? + { // the last SCRIPT_JUSTIFY_xxx + // yes // == 15 (usp 1.6) + rVisualItem.mbHasKashidas = true; + // so prepare for kashida handling + InitKashidaHandling(); break; } - if ( bHasKashida ) + + if( rVisualItem.HasKashidas() ) for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i ) { + // TODO: 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. @@ -2363,7 +2464,6 @@ void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) } } - // convert virtual charwidths to glyph justification values HRESULT nRC = (*pScriptApplyLogicalWidth)( mpCharWidths + rVisualItem.mnMinCharPos, @@ -2383,24 +2483,23 @@ void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) break; } - // TODO: for kashida justification - // check the widths which are added to mpJustification - // if added width is smaller than iKashidaWidth returned by - // ScriptGetFontProperties, do something (either enlarge to - // iKashidaWidth, or reduce to original width). - // 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( rVisualItem.mbHasKashidas ) + KashidaItemFix( nMinGlyphPos, nEndGlyphPos ); + } + + // workaround needed for older USP versions: // right align the justification-adjusted glyphs in their cells for RTL-items - if( bManualCellAlign && rVisualItem.mpScriptItem->a.fRTL && !bHasKashida ) + // unless the right alignment is done by inserting kashidas + if( bManualCellAlign && rVisualItem.mpScriptItem->a.fRTL && !rVisualItem.HasKashidas() ) { for( i = nMinGlyphPos; i < nEndGlyphPos; ++i ) { @@ -2417,17 +2516,164 @@ void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs ) // ----------------------------------------------------------------------- +void UniscribeLayout::InitKashidaHandling() +{ + if( mnMinKashidaGlyph != 0 ) // already initialized + return; + + mrWinFontEntry.InitKashidaHandling( mhDC ); + mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth()); + mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph(); +} + +// adjust the kashida placement matching to the WriterEngine +void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos ) +{ + // workaround needed for all known USP versions: + // ApplyLogicalWidth does not match ScriptJustify behaviour + for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i ) + { + // check for vowels + if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ]) + && (1U << mpVisualAttrs[i].uJustification) & 0xFF89 ) + { + // vowel, we do it like ScriptJustify does + // the vowel gets the extra width + long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; + mpJustifications [ i ] = mpGlyphAdvances [ i ]; + mpJustifications [ i - 1 ] += nSpaceAdded; + } + } + + // redistribute the widths for kashidas + for( int i = nMinGlyphPos; i < nEndGlyphPos; ) + KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i ); +} + +bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos ) +{ + // doing pixel work within a word. + // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth + + // find the next kashida + int nMinPos = *pnCurrentPos; + int nMaxPos = *pnCurrentPos; + for( int i = nMaxPos; i < nEndGlyphPos; ++i ) + { + if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK) + && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) ) + break; + nMaxPos = i; + } + *pnCurrentPos = nMaxPos + 1; + if( nMinPos == nMaxPos ) + return false; + + // calculate the available space for an extra kashida + long nMaxAdded = 0; + int nKashPos = -1; + for( int i = nMaxPos; i >= nMinPos; --i ) + { + long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; + if( nSpaceAdded > nMaxAdded ) + { + nKashPos = i; + nMaxAdded = nSpaceAdded; + } + } + + // return early if there is no need for an extra kashida + if ( nMaxAdded <= 0 ) + return false; + // return early if there is not enough space for an extra kashida + if( 2*nMaxAdded < mnMinKashidaWidth ) + return false; + + // redistribute the extra spacing to the kashida position + for( int i = nMinPos; i <= nMaxPos; ++i ) + { + if( i == nKashPos ) + continue; + // everything else should not have extra spacing + long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ]; + if( nSpaceAdded > 0 ) + { + mpJustifications[ i ] -= nSpaceAdded; + mpJustifications[ nKashPos ] += nSpaceAdded; + } + } + + // check if we fulfill minimal kashida width + long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ]; + if( nSpaceAdded < mnMinKashidaWidth ) + { + // ugly: steal some pixels + long nSteal = 1; + if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos))) + nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos); + for( int i = nMinPos; i <= nMaxPos; ++i ) + { + if( i == nKashPos ) + continue; + nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal ); + if ( nSteal > 0 ) + { + mpJustifications [ i ] -= nSteal; + mpJustifications [ nKashPos ] += nSteal; + nSpaceAdded += nSteal; + } + if( nSpaceAdded >= mnMinKashidaWidth ) + return true; + } + } + + // blank padding + long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded; + if( nSpaceMissing > 0 ) + { + // inner glyph: distribute extra space evenly + if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) ) + { + mpJustifications [ nKashPos ] += nSpaceMissing; + long nHalfSpace = nSpaceMissing / 2; + mpJustifications [ nMinPos - 1 ] -= nHalfSpace; + mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace; + } + // rightmost: left glyph gets extra space + else if( nMinPos > nMinGlyphPos ) + { + mpJustifications [ nMinPos - 1 ] -= nSpaceMissing; + mpJustifications [ nKashPos ] += nSpaceMissing; + } + // leftmost: right glyph gets extra space + else if( nMaxPos < nEndGlyphPos - 1 ) + { + mpJustifications [ nKashPos ] += nSpaceMissing; + mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing; + } + else + return false; + } + + return true; +} + +// ----------------------------------------------------------------------- + void UniscribeLayout::Justify( long nNewWidth ) { long nOldWidth = 0; int i; for( i = mnMinCharPos; i < mnEndCharPos; ++i ) nOldWidth += mpCharWidths[ i ]; + if( nOldWidth <= 0 ) + return; - nNewWidth *= mnUnitsPerPixel; + nNewWidth *= mnUnitsPerPixel; // convert into font units if( nNewWidth == nOldWidth ) return; - double fStretch = (double)nNewWidth / nOldWidth; + // prepare to distribute the extra width evenly among the visual items + const double fStretch = (double)nNewWidth / nOldWidth; // initialize justifications array mpJustifications = new int[ mnGlyphCapacity ]; @@ -2451,18 +2697,12 @@ void UniscribeLayout::Justify( long nNewWidth ) nItemWidth += mpCharWidths[ i ]; nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5); - SCRIPT_FONTPROPERTIES aFontProperties; - int nMinKashida = 1; - HRESULT nRC = (*pScriptGetFontProperties)( mhDC, &rScriptCache, &aFontProperties ); - if( !nRC ) - nMinKashida = aFontProperties.iKashidaWidth; - - nRC = (*pScriptJustify) ( + HRESULT nRC = (*pScriptJustify) ( mpVisualAttrs + rVisualItem.mnMinGlyphPos, mpGlyphAdvances + rVisualItem.mnMinGlyphPos, rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos, nItemWidth, - nMinKashida, + mnMinKashidaWidth, mpJustifications + rVisualItem.mnMinGlyphPos ); rVisualItem.mnXOffset = nXOffset; @@ -2471,6 +2711,45 @@ void UniscribeLayout::Justify( long nNewWidth ) } } +// ----------------------------------------------------------------------- + +bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const +{ + // we have to find the visual item first since the mpLogClusters[] + // needed to find the cluster start is relative to to the visual item + int nMinGlyphIndex = -1; + for( int nItem = 0; nItem < mnItemCount; ++nItem ) + { + const VisualItem& rVisualItem = mpVisualItems[ nItem ]; + if( (nCharPos >= rVisualItem.mnMinCharPos) + && (nCharPos < rVisualItem.mnEndCharPos) ) + { + nMinGlyphIndex = rVisualItem.mnMinGlyphPos; + break; + } + } + // Invalid char pos or leftmost glyph in visual item + if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] ) + return false; + +// This test didn't give the expected results +/* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ]) + // two chars, one glyph + return false;*/ + + const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex; + if( nGlyphPos <= 0 ) + return true; + // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE + // and not SCRIPT_JUSTIFY_ARABIC_BLANK + // special case: glyph to the left is vowel (no advance width) + if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK + || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE + && mpGlyphAdvances [ nGlyphPos-1 ] )) + return false; + return true; +} + #endif // USE_UNISCRIBE // ======================================================================= @@ -2519,13 +2798,26 @@ SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLe return pWinLayout; } +// ----------------------------------------------------------------------- + +int WinSalGraphics::GetMinKashidaWidth() +{ + if( !mpWinFontEntry[0] ) + return 0; + mpWinFontEntry[0]->InitKashidaHandling( mhDC ); + int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth()); + return nMinKashida; +} + // ======================================================================= ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD ) -: ImplFontEntry( rFSD ), - maWidthMap( 512 ), - mpKerningPairs( NULL ), - mnKerningPairs( -1 ) +: ImplFontEntry( rFSD ) +, maWidthMap( 512 ) +, mpKerningPairs( NULL ) +, mnKerningPairs( -1 ) +, mnMinKashidaWidth( -1 ) +, mnMinKashidaGlyph( -1 ) { #ifdef USE_UNISCRIBE maScriptCache = NULL; @@ -2582,6 +2874,33 @@ int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const return nKernAmount; } +// ----------------------------------------------------------------------- + +bool ImplWinFontEntry::InitKashidaHandling( HDC hDC ) +{ + if( mnMinKashidaWidth >= 0 ) // already cached? + return mnMinKashidaWidth; + + // initialize the kashida width + mnMinKashidaWidth = 0; + mnMinKashidaGlyph = 0; +#ifdef USE_UNISCRIBE + if (aUspModule || (bUspEnabled && InitUSP())) + { + SCRIPT_FONTPROPERTIES aFontProperties; + aFontProperties.cBytes = sizeof (aFontProperties); + SCRIPT_CACHE& rScriptCache = GetScriptCache(); + HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties ); + if( nRC != 0 ) + return false; + mnMinKashidaWidth = aFontProperties.iKashidaWidth; + mnMinKashidaGlyph = aFontProperties.wgKashida; + } +#endif // USE_UNISCRIBE + + return true; +} + // ======================================================================= ImplFontData* ImplWinFontData::Clone() const |